FFNet.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790
  1. /* FFNet.cpp
  2. *
  3. * Copyright (C) 1997-2017 David Weenink
  4. *
  5. * This code is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /*
  19. djmw 20020712 GPL header
  20. djmw 20040420 Modified FFNet_create and FFNet_init parameters.
  21. djmw 20040422 FFNet_drawActivation: nodes with activity > 0.05 had incorrect size.
  22. djmw 20040422 FFNet_extractWeights added.
  23. djmw 20040425 FFNet_drawTopology fill input units; increase distance from arrow for output labels
  24. djmw 20040513 Info changes.
  25. djmw 20040526 Adapted FFNet_drawCostHistory.
  26. djmw 20050131 Reversed sign of derivative in minimumCrossEntropy.
  27. djmw 20061212 Changed info to Melder_writeLine<x> format.
  28. djmw 20070902 FFNet_createNameFromTopology to wchar
  29. djmw 20071014 Melder_error<n>
  30. djmw 20080121 float -> double
  31. djmw 20110304 Thing_new
  32. */
  33. #include "FFNet_Matrix.h"
  34. #include "Matrix_extensions.h"
  35. #include "TableOfReal_extensions.h"
  36. #include "PatternList.h"
  37. #include "Collection.h"
  38. #include "Categories.h"
  39. static void bookkeeping (FFNet me);
  40. #include "oo_DESTROY.h"
  41. #include "FFNet_def.h"
  42. #include "oo_COPY.h"
  43. #include "FFNet_def.h"
  44. #include "oo_EQUAL.h"
  45. #include "FFNet_def.h"
  46. #include "oo_CAN_WRITE_AS_ENCODING.h"
  47. #include "FFNet_def.h"
  48. #include "oo_WRITE_TEXT.h"
  49. #include "FFNet_def.h"
  50. #include "oo_WRITE_BINARY.h"
  51. #include "FFNet_def.h"
  52. #include "oo_READ_TEXT.h"
  53. #include "FFNet_def.h"
  54. #include "oo_READ_BINARY.h"
  55. #include "FFNet_def.h"
  56. #include "oo_DESCRIPTION.h"
  57. #include "FFNet_def.h"
  58. Thing_implement (FFNet, Daata, 0);
  59. autostring32 FFNet_createNameFromTopology (FFNet me) {
  60. autoMelderString name;
  61. MelderString_copy (& name, my nUnitsInLayer [0]);
  62. for (integer i = 1; i <= my nLayers; i ++) {
  63. MelderString_appendCharacter (& name, U'-');
  64. MelderString_append (& name, my nUnitsInLayer [i]);
  65. }
  66. return Melder_dup (name.string);
  67. }
  68. /****** non-linearities ****************************************************/
  69. static double sigmoid (FFNet /*me*/, double x, double *deriv) {
  70. double act = NUMsigmoid (x);
  71. *deriv = act * (1.0 - act);
  72. return act;
  73. }
  74. /* ******************* cost functions ****************************************/
  75. /*
  76. For the errors calculated in the cost functions:
  77. if target > activity ==> error > 0
  78. if target < activity ==> error < 0
  79. */
  80. static double minimumSquaredError (FFNet me, const double target []) {
  81. integer k = my nNodes - my nOutputs + 1;
  82. double cost = 0.0;
  83. for (integer i = 1; i <= my nOutputs; i ++, k ++) {
  84. double e = my error [k] = target [i] - my activity [k];
  85. cost += e * e;
  86. }
  87. return 0.5 * cost;
  88. }
  89. /* E = - sum (i=1; i=nPatterns; sum (k=1;k=nOutputs; t [k]*ln (o [k]) + (1-t [k])ln (1-o [k]))) */
  90. /* dE/do [k] = -(1-t [k])/ (1-o [k]) + t [k]/o [k] */
  91. /* werkt niet bij (grote?) netten */
  92. static double minimumCrossEntropy (FFNet me, const double target []) {
  93. integer k = my nNodes - my nOutputs + 1;
  94. double cost = 0.0;
  95. for (integer i = 1; i <= my nOutputs; i ++, k ++) {
  96. double t1 = 1.0 - target [i];
  97. double o1 = 1.0 - my activity [k];
  98. cost -= target [i] * log (my activity [k]) + t1 * log (o1);
  99. my error [k] = -t1 / o1 + target [i] / my activity [k];
  100. }
  101. return cost;
  102. }
  103. /* *********************************************************************** */
  104. static void bookkeeping (FFNet me) {
  105. integer nWeights = 0;
  106. my nNodes = my nUnitsInLayer [0];
  107. for (integer i = 1; i <= my nLayers; i ++) {
  108. my nNodes += my nUnitsInLayer [i] + 1;
  109. nWeights += my nUnitsInLayer [i] * (my nUnitsInLayer [i - 1] + 1);
  110. }
  111. if (my nWeights > 0 && my nWeights != nWeights) {
  112. Melder_throw (U"Number of weights is incorret.");
  113. }
  114. my nWeights = nWeights;
  115. // The following test is essential because when an FFNet is read from file the w array already exists
  116. if (! my w) {
  117. my w = NUMvector<double> (1, my nWeights);
  118. }
  119. my activity = NUMvector<double> (1, my nNodes);
  120. my isbias = NUMvector<integer> (1, my nNodes);
  121. my nodeFirst = NUMvector<integer> (1, my nNodes);
  122. my nodeLast = NUMvector<integer> (1, my nNodes);
  123. my wFirst = NUMvector<integer> (1, my nNodes);
  124. my wLast = NUMvector<integer> (1, my nNodes);
  125. my wSelected = NUMvector<integer> (1, my nWeights);
  126. my error = NUMvector<double> (1, my nNodes);
  127. my deriv = NUMvector<double> (1, my nNodes);
  128. my dwi = NUMvector<double> (1, my nWeights);
  129. my dw = NUMvector<double> (1, my nWeights);
  130. my nInputs = my nUnitsInLayer [0];
  131. my nOutputs = my nUnitsInLayer [my nLayers];
  132. my isbias [my nInputs + 1] = 1;
  133. my activity [my nInputs + 1] = 1.0;
  134. integer n = my nUnitsInLayer [0] + 2;
  135. integer firstNodeInPrevious = 1, lastWeightInPrevious = 0;
  136. for (integer j = 1; j <= my nLayers; j ++, n ++) {
  137. for (integer i = 1; i <= my nUnitsInLayer [j]; i ++, n ++) {
  138. my isbias [n] = 0;
  139. my nodeFirst [n] = firstNodeInPrevious;
  140. my nodeLast [n] = my nodeFirst [n] + my nUnitsInLayer [j - 1];
  141. my wFirst [n] = lastWeightInPrevious + (i - 1) * (my nUnitsInLayer [j - 1] + 1) + 1;
  142. my wLast [n] = my wFirst [n] + my nUnitsInLayer [j - 1];
  143. }
  144. if (j != my nLayers) {
  145. my isbias [n] = 1;
  146. my activity [n] = 1.0;
  147. }
  148. lastWeightInPrevious = my wLast [n - 1];
  149. firstNodeInPrevious += my nUnitsInLayer [j - 1] + 1;
  150. }
  151. FFNet_selectAllWeights (me);
  152. }
  153. void structFFNet :: v_info () {
  154. our structDaata :: v_info ();
  155. MelderInfo_writeLine (U"Number of layers: ", our nLayers);
  156. MelderInfo_writeLine (U"Total number of units: ", FFNet_getNumberOfUnits (this));
  157. MelderInfo_writeLine (U" Number of units in layer ", our nLayers, U" (output): ", our nUnitsInLayer [nLayers]);
  158. for (integer i = our nLayers - 1; i >= 1; i --) {
  159. MelderInfo_writeLine (U" Number of units in layer ", i, U" (hidden): ", our nUnitsInLayer [i]);
  160. }
  161. MelderInfo_writeLine (U" Number of units in layer 0 (input): ", our nUnitsInLayer [0]);
  162. MelderInfo_writeLine (U"Outputs are linear: ", Melder_boolean (our outputsAreLinear));
  163. MelderInfo_writeLine (U"Number of weights: ", our nWeights, U" (",
  164. FFNet_dimensionOfSearchSpace (this), U" selected)");
  165. MelderInfo_writeLine (U"Number of nodes: ", our nNodes);
  166. }
  167. void FFNet_init (FFNet me, integer numberOfInputs, integer nodesInLayer1, integer nodesInLayer2, integer numberOfOutputs, bool outputsAreLinear) {
  168. integer numberOfLayers = 3;
  169. Melder_require (numberOfInputs > 0, U"Number of inputs should be greater than zero.");
  170. Melder_require (numberOfOutputs > 0, U"Number of outputs should be greater than zero.");
  171. if (nodesInLayer1 < 1) {
  172. numberOfLayers --;
  173. }
  174. if (nodesInLayer2 < 1) {
  175. numberOfLayers --;
  176. }
  177. my nLayers = numberOfLayers;
  178. my nUnitsInLayer = NUMvector<integer> (0, numberOfLayers);
  179. my nUnitsInLayer [numberOfLayers --] = numberOfOutputs;
  180. if (nodesInLayer2 > 0) {
  181. my nUnitsInLayer [numberOfLayers --] = nodesInLayer2;
  182. }
  183. if (nodesInLayer1 > 0) {
  184. my nUnitsInLayer [numberOfLayers --] = nodesInLayer1;
  185. }
  186. my nUnitsInLayer [numberOfLayers] = numberOfInputs;
  187. Melder_assert (numberOfLayers == 0);
  188. my outputsAreLinear = outputsAreLinear;
  189. bookkeeping (me);
  190. FFNet_setCostFunction (me, FFNet_COST_MSE);
  191. FFNet_setNonLinearity (me, FFNet_NONLIN_SIGMOID);
  192. FFNet_reset (me, 0.1);
  193. }
  194. void FFNet_setOutputCategories (FFNet me, Categories thee) {
  195. autoCategories uniq = Categories_selectUniqueItems (thee);
  196. if (uniq->size == thy size) {
  197. my outputCategories = uniq.move();
  198. }
  199. }
  200. autoFFNet FFNet_create (integer numberOfInputs, integer numberInLayer1, integer numberInLayer2, integer numberOfOutputs, bool outputsAreLinear) {
  201. try {
  202. autoFFNet me = Thing_new (FFNet);
  203. FFNet_init (me.get(), numberOfInputs, numberInLayer1, numberInLayer2, numberOfOutputs, outputsAreLinear);
  204. return me;
  205. } catch (MelderError) {
  206. Melder_throw (U"FFNet not created.");
  207. }
  208. }
  209. void FFNet_setNonLinearity (FFNet me, int nonLinearityType) {
  210. my nonLinearityType = nonLinearityType;
  211. my nonLinearity = sigmoid;
  212. my nlClosure = nullptr;
  213. }
  214. void FFNet_setCostFunction (FFNet me, int costType) {
  215. my costFunctionType = costType;
  216. if (costType == 2) {
  217. my costFunction = minimumCrossEntropy;
  218. } else {
  219. my costFunction = minimumSquaredError;
  220. }
  221. my cfClosure = nullptr;
  222. }
  223. double FFNet_getBias (FFNet me, integer layer, integer unit) {
  224. try {
  225. integer node = FFNet_getNodeNumberFromUnitNumber (me, unit, layer);
  226. Melder_require (node > 0, U"Not a valid unit / layer combination.");
  227. integer bias_unit = my wLast [node];
  228. return my w [bias_unit];
  229. } catch (MelderError) {
  230. return undefined;
  231. }
  232. }
  233. void FFNet_setBias (FFNet me, integer layer, integer unit, double value) {
  234. integer node = FFNet_getNodeNumberFromUnitNumber (me, unit, layer);
  235. Melder_require (node > 0, U"Not a valid unit / layer combination.");
  236. integer bias_unit = my wLast [node]; // ??? +1
  237. my w [bias_unit] = value;
  238. }
  239. void FFNet_setWeight (FFNet me, integer layer, integer unit, integer unit_from, double value) {
  240. integer node = FFNet_getNodeNumberFromUnitNumber (me, unit, layer);
  241. Melder_require (node > 0, U"Not a valid unit / layer combination.");
  242. integer nodef = FFNet_getNodeNumberFromUnitNumber (me, unit_from, layer - 1);
  243. Melder_require (nodef > 0, U"Not a valid unit / layer combination.");
  244. integer w_unit = my wFirst [node] + unit_from - 1;
  245. my w [w_unit] = value;
  246. }
  247. double FFNet_getWeight (FFNet me, integer layer, integer unit, integer unit_from) {
  248. integer node = FFNet_getNodeNumberFromUnitNumber (me, unit, layer);
  249. Melder_require (node > 0, U"Not a valid unit / layer combination.");
  250. integer nodef = FFNet_getNodeNumberFromUnitNumber (me, unit_from, layer - 1);
  251. Melder_require (nodef > 0, U"Not a valid unit / layer combination.");
  252. integer w_unit = my wFirst [node] + unit_from - 1;
  253. return my w [w_unit];
  254. }
  255. void FFNet_reset (FFNet me, double weightRange) {
  256. for (integer i = 1; i <= my nWeights; i ++) {
  257. if (my wSelected [i]) {
  258. my w [i] = NUMrandomUniform (- weightRange, weightRange);
  259. }
  260. }
  261. for (integer i = 1; i <= my nNodes; i ++) {
  262. my activity [i] = ( my isbias [i] ? 1.0 : 0.0 );
  263. }
  264. my accumulatedCost = 0.0;
  265. my minimizer.reset();
  266. }
  267. conststring32 FFNet_getCategoryOfOutputUnit (FFNet me, integer outputUnit) {
  268. conststring32 result = U"-- undefined --";
  269. if (my outputCategories && outputUnit <= my outputCategories -> size) {
  270. SimpleString ss = my outputCategories->at [outputUnit];
  271. result = ss -> string.get();
  272. }
  273. return result;
  274. }
  275. integer FFNet_getOutputUnitOfCategory (FFNet me, const char32* category) {
  276. integer result = 0;
  277. if (my outputCategories) {
  278. for (integer i = 1; i <= my outputCategories -> size; i ++) {
  279. SimpleString s = my outputCategories->at [i];
  280. if (Melder_equ (s -> string.get(), category)) {
  281. result = i;
  282. break;
  283. }
  284. }
  285. }
  286. return result;
  287. }
  288. /***** OPERATION: ***********************************************************/
  289. /* step 1 */
  290. void FFNet_propagate (FFNet me, const double input [], double output []) {
  291. // clamp input pattern on the network
  292. for (integer i = 1; i <= my nUnitsInLayer [0]; i ++) {
  293. my activity [i] = input [i];
  294. }
  295. // on hidden units use activation function
  296. integer k = 1, nNodes = my outputsAreLinear ? my nNodes - my nOutputs : my nNodes;
  297. for (integer i = my nUnitsInLayer [0] + 2; i <= nNodes; i ++) {
  298. if (my isbias [i]) {
  299. continue;
  300. }
  301. double act = 0.0;
  302. for (integer j = my nodeFirst [i]; j <= my nodeLast [i]; j ++, k ++) {
  303. act += my w [k] * my activity [j];
  304. }
  305. my activity [i] = my nonLinearity (me, act, & my deriv [i]);
  306. }
  307. // on output units use another activation function
  308. if (my outputsAreLinear) {
  309. for (integer i = nNodes + 1; i <= my nNodes; i ++) {
  310. if (my isbias [i]) {
  311. continue;
  312. }
  313. double act = 0.0;
  314. for (integer j = my nodeFirst [i]; j <= my nodeLast [i]; j ++, k ++) {
  315. act += my w [k] * my activity [j];
  316. }
  317. my activity [i] = act;
  318. my deriv [i] = 1.0;
  319. }
  320. }
  321. k = my nNodes - my nOutputs + 1;
  322. if (output) {
  323. for (integer i = 1; i <= my nOutputs; i ++, k ++) {
  324. output [i] = my activity [k];
  325. }
  326. }
  327. }
  328. double FFNet_computeError (FFNet me, const double target []) {
  329. // compute error at output layer
  330. double cost = my costFunction (me, target);
  331. for (integer i = 1; i <= my nNodes - my nOutputs; i ++) {
  332. my error [i] = 0.0;
  333. }
  334. // backpropagation of errors from output to first hidden layer
  335. for (integer i = my nNodes; i > my nInputs + 1; i--) {
  336. if (my isbias [i]) {
  337. continue;
  338. }
  339. my error [i] *= my deriv [i];
  340. if (my nodeFirst [i] > my nInputs + 1) {
  341. integer k = my wFirst [i];
  342. for (integer j = my nodeFirst [i]; j <= my nodeLast [i] - 1; j ++, k ++) {
  343. my error [j] += my error [i] * my w [k];
  344. }
  345. }
  346. }
  347. return cost;
  348. }
  349. void FFNet_computeDerivative (FFNet me) {
  350. integer k = 1;
  351. for (integer i = my nInputs + 2; i <= my nNodes; i ++) {
  352. if (! my isbias [i]) {
  353. for (integer j = my nodeFirst [i]; j <= my nodeLast [i]; j ++, k ++) {
  354. my dwi [k] = - my error [i] * my activity [j];
  355. }
  356. }
  357. }
  358. }
  359. /******* end operation ******************************************************/
  360. integer FFNet_getWinningUnit (FFNet me, int labeling) {
  361. integer pos = 1, k = my nNodes - my nOutputs;
  362. if (labeling == 2) { /* stochastic */
  363. double sum = 0.0;
  364. for (integer i = 1; i <= my nOutputs; i ++) {
  365. sum += my activity [k + i];
  366. }
  367. double random = NUMrandomUniform (0.0, sum);
  368. for (pos = my nOutputs; pos >= 2; pos--) {
  369. if (random > (sum -= my activity [k + pos])) {
  370. break;
  371. }
  372. }
  373. } else { /* winner-takes-all */
  374. double max = my activity [k + 1];
  375. for (integer i = 2; i <= my nOutputs; i ++) if (my activity [k + i] > max) {
  376. max = my activity [k + i];
  377. pos = i;
  378. }
  379. }
  380. return pos;
  381. }
  382. void FFNet_propagateToLayer (FFNet me, const double input [], double activity [], integer layer) {
  383. Melder_assert (activity);
  384. integer k = 0;
  385. FFNet_propagate (me, input, nullptr);
  386. for (integer i = 0; i < layer; i ++) {
  387. k += my nUnitsInLayer [i] + 1;
  388. }
  389. for (integer i = 1; i <= my nUnitsInLayer [layer]; i ++) {
  390. activity [i] = my activity [k + i];
  391. }
  392. }
  393. void FFNet_selectAllWeights (FFNet me) {
  394. for (integer i = 1; i <= my nWeights; i ++) {
  395. my wSelected [i] = 1;
  396. }
  397. my dimension = my nWeights;
  398. }
  399. integer FFNet_dimensionOfSearchSpace (FFNet me) {
  400. integer n = 0;
  401. for (integer i = 1; i <= my nWeights; i ++) {
  402. if (my wSelected [i]) {
  403. n ++;
  404. }
  405. }
  406. return n;
  407. }
  408. void FFNet_selectBiasesInLayer (FFNet me, integer layer) {
  409. integer node = my nUnitsInLayer [0] + 1;
  410. if (layer < 1 || layer > my nLayers) {
  411. return;
  412. }
  413. for (integer i = 1; i <= my nWeights; i ++) {
  414. my wSelected [i] = 0.0;
  415. }
  416. for (integer i = 1; i < layer; i ++) {
  417. node += my nUnitsInLayer [i] + 1;
  418. }
  419. for (integer i = node + 1; i <= node + my nUnitsInLayer [layer]; i ++) {
  420. my wSelected [my wLast [i]] = 1;
  421. }
  422. my dimension = my nUnitsInLayer [layer];
  423. }
  424. void FFNet_weightConnectsUnits (FFNet me, integer index, integer *fromUnit, integer *toUnit, integer *layer) {
  425. Melder_assert (index > 0 && index <= my nWeights);
  426. integer i = 1, np = 0, nw = my nUnitsInLayer [1] * (my nInputs + 1);
  427. while (index > nw) {
  428. i ++;
  429. nw += (np = my nUnitsInLayer [i] * (my nUnitsInLayer [i - 1] + 1));
  430. }
  431. if (i > 1) {
  432. index -= nw - np;
  433. }
  434. if (fromUnit) {
  435. *fromUnit = index % (my nUnitsInLayer [i - 1] + 1);
  436. }
  437. if (toUnit) {
  438. *toUnit = (index - 1) / (my nUnitsInLayer [i - 1] + 1) + 1;
  439. }
  440. if (layer) {
  441. *layer = i;
  442. }
  443. }
  444. integer FFNet_getNodeNumberFromUnitNumber (FFNet me, integer unit, integer layer) {
  445. if (layer < 0 || layer > my nLayers || unit > my nUnitsInLayer [layer]) {
  446. return -1;
  447. }
  448. integer node = unit;
  449. for (integer i = 0; i < layer; i ++) {
  450. node += my nUnitsInLayer [i] + 1;
  451. }
  452. return node;
  453. }
  454. void FFNet_nodeToUnitInLayer (FFNet me, integer node, integer *unit, integer *layer) {
  455. Melder_assert (node > 0 && node <= my nNodes);
  456. integer i = 0, nn = my nUnitsInLayer [0] + 1;
  457. while (node > nn) {
  458. nn += my nUnitsInLayer [ ++i] + 1;
  459. }
  460. if (i > 0) {
  461. node -= nn - (my nUnitsInLayer [i] + 1);
  462. }
  463. *unit = node % (my nUnitsInLayer [i] + 1);
  464. *layer = i;
  465. }
  466. integer FFNet_getNumberOfWeights (FFNet me) {
  467. return my nWeights;
  468. }
  469. integer FFNet_getNumberOfLayers (FFNet me) {
  470. return my nLayers;
  471. }
  472. integer FFNet_getNumberOfUnits (FFNet me) {
  473. return my nNodes - my nLayers;
  474. }
  475. integer FFNet_getNumberOfHiddenLayers (FFNet me) {
  476. return my nLayers - 1;
  477. }
  478. integer FFNet_getNumberOfUnitsInLayer (FFNet me, int layer) {
  479. if (layer > my nLayers || layer < 0) {
  480. return 0;
  481. }
  482. return my nUnitsInLayer [layer];
  483. }
  484. double FFNet_getMinimum (FFNet me) {
  485. return ( my minimizer ? Minimizer_getMinimum (my minimizer.get()) : undefined );
  486. }
  487. void FFNet_drawTopology (FFNet me, Graphics g) {
  488. integer maxNumOfUnits = my nUnitsInLayer [0];
  489. int dxIsFixed = 1;
  490. double dy = 1.0 / (my nLayers + 1);
  491. for (integer i = 1; i <= my nLayers; i ++) {
  492. if (my nUnitsInLayer [i] > maxNumOfUnits) {
  493. maxNumOfUnits = my nUnitsInLayer [i];
  494. }
  495. }
  496. double dx = 1.0 / maxNumOfUnits;
  497. double radius = dx / 10.0;
  498. Graphics_setInner (g);
  499. Graphics_setWindow (g, 0.0, 1.0, 0.0, 1.0);
  500. for (integer i = 0; i <= my nLayers; i ++) {
  501. double dx2 = dx, x2WC, y2WC = dy / 2 + i * dy;
  502. double x2 = (maxNumOfUnits - my nUnitsInLayer [i] + 1) * dx2 / 2;
  503. /* draw the units */
  504. if (! dxIsFixed) {
  505. dx2 = 1.0 / my nUnitsInLayer [i];
  506. x2 = dx2 / 2.0;
  507. }
  508. if (i == 0) {
  509. Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_TOP);
  510. x2WC = x2;
  511. for (integer j = 1; j <= my nInputs; j ++) {
  512. Graphics_arrow (g, x2WC, y2WC - radius - dy / 4.0, x2WC, y2WC - radius);
  513. x2WC += dx2;
  514. }
  515. }
  516. Graphics_setColour (g, Graphics_RED);
  517. x2WC = x2;
  518. for (integer j = 1; j <= my nUnitsInLayer [i]; j ++) {
  519. Graphics_circle (g, x2WC, y2WC, radius);
  520. if (i > 0) {
  521. Graphics_fillCircle (g, x2WC, y2WC, radius);
  522. }
  523. x2WC += dx2;
  524. }
  525. Graphics_setColour (g, Graphics_BLACK);
  526. if (i > 0) {
  527. double dx1 = dx;
  528. double x1 = (maxNumOfUnits - my nUnitsInLayer [i - 1] + 1) * dx1 / 2.0;
  529. double y1WC = y2WC - dy;
  530. if (! dxIsFixed) {
  531. dx1 = 1.0 / my nUnitsInLayer [i - 1];
  532. x1 = dx1 / 2.0;
  533. }
  534. x2WC = x2;
  535. for (integer j = 1; j <= my nUnitsInLayer [i]; j ++) {
  536. double x1WC = x1;
  537. for (integer k = 1; k <= my nUnitsInLayer [i - 1]; k ++) {
  538. double xd = x2WC - x1WC;
  539. double cosa = xd / sqrt (xd * xd + dy * dy);
  540. double sina = dy / sqrt (xd * xd + dy * dy);
  541. Graphics_line (g, x1WC + radius * cosa, y1WC + radius * sina, x2WC - radius * cosa, y2WC - radius * sina);
  542. x1WC += dx1;
  543. }
  544. x2WC += dx2;
  545. }
  546. }
  547. if (i == my nLayers) {
  548. x2WC = x2;
  549. Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_BOTTOM);
  550. for (integer j = 1; j <= my nOutputs; j ++) {
  551. Graphics_arrow (g, x2WC, y2WC + radius, x2WC, y2WC + radius + dy / 4.0);
  552. if (my outputCategories) {
  553. Categories_drawItem (my outputCategories.get(), g, j, x2WC, y2WC + radius + dy / 4.0);
  554. }
  555. x2WC += dx2;
  556. }
  557. }
  558. }
  559. Graphics_unsetInner (g);
  560. }
  561. void FFNet_drawActivation (FFNet me, Graphics g) {
  562. integer node = 1, maxNumOfUnits = my nUnitsInLayer [0];
  563. int dxIsFixed = 1;
  564. Graphics_Colour colour = Graphics_inqColour (g);
  565. double dy = 1.0 / (my nLayers + 1);
  566. Graphics_setInner (g);
  567. Graphics_setWindow (g, 0.0, 1.0, 0.0, 1.0);
  568. for (integer i = 1; i <= my nLayers; i ++) {
  569. if (my nUnitsInLayer [i] > maxNumOfUnits) {
  570. maxNumOfUnits = my nUnitsInLayer [i];
  571. }
  572. }
  573. double dx = 1.0 / maxNumOfUnits;
  574. double r1 = dx / 2.0; /* May touch when neighbouring activities are both 1 (very rare). */
  575. for (integer i = 0; i <= my nLayers; i ++, node ++) {
  576. double dx2 = dx, x2WC, y2WC = dy / 2.0 + i * dy;
  577. double x2 = (maxNumOfUnits - my nUnitsInLayer [i] + 1) * dx2 / 2.0;
  578. if (! dxIsFixed) {
  579. dx2 = 1.0 / my nUnitsInLayer [i];
  580. x2 = dx2 / 2.0;
  581. }
  582. x2WC = x2;
  583. for (integer j = 1; j <= my nUnitsInLayer [i]; j ++, node ++) {
  584. double activity = my activity [node];
  585. double radius = r1 * (fabs (activity) < 0.05 ? 0.05 : fabs (activity));
  586. /*Graphics_setColour (g, activity < 0 ? Graphics_BLACK : Graphics_RED);*/
  587. Graphics_circle (g, x2WC, y2WC, radius);
  588. if (activity < 0) {
  589. Graphics_fillCircle (g, x2WC, y2WC, radius);
  590. }
  591. x2WC += dx2;
  592. }
  593. }
  594. Graphics_setColour (g, colour);
  595. Graphics_unsetInner (g);
  596. }
  597. /* This routine is deprecated since praat-4.2.4 20040422 and will be removed in the future. */
  598. void FFNet_drawWeightsToLayer (FFNet me, Graphics g, int layer, int scaling, int garnish) {
  599. Melder_require (layer > 0 && layer <= my nLayers, U"Layer number should be between 1 and ", my nLayers, U".");
  600. autoMatrix weights = FFNet_weightsToMatrix (me, layer, false);
  601. Matrix_scale (weights.get(), scaling);
  602. Matrix_drawAsSquares (weights.get(), g, 0.0, 0.0, 0.0, 0.0, 0);
  603. if (garnish) {
  604. double x1WC, x2WC, y1WC, y2WC;
  605. Graphics_inqWindow (g, & x1WC, & x2WC, & y1WC, & y2WC);
  606. Graphics_textBottom (g, false, Melder_cat (U"Units in layer ", layer, U" ->"));
  607. if (layer == 1) {
  608. Graphics_textLeft (g, false, U"Input units ->");
  609. } else {
  610. Graphics_textLeft (g, false, Melder_cat (U"Units in layer ", layer - 1, U" ->"));
  611. }
  612. /* how do I find out the current settings ??? */
  613. Graphics_setTextAlignment (g, Graphics_RIGHT, Graphics_HALF);
  614. Graphics_setInner (g);
  615. Graphics_text (g, 0.5, weights->ny, U"bias");
  616. Graphics_unsetInner (g);
  617. }
  618. }
  619. void FFNet_drawWeights (FFNet me, Graphics g, integer layer, int garnish) {
  620. autoTableOfReal thee = FFNet_extractWeights (me, layer);
  621. TableOfReal_drawAsSquares (thee.get(), g, 1, thy numberOfRows, 1, thy numberOfColumns, garnish);
  622. }
  623. void FFNet_drawCostHistory (FFNet me, Graphics g, integer iFrom, integer iTo, double costMin, double costMax, int garnish) {
  624. if (my minimizer) {
  625. Minimizer_drawHistory (my minimizer.get(), g, iFrom, iTo, costMin, costMax, 0);
  626. }
  627. if (garnish) {
  628. Graphics_drawInnerBox (g);
  629. Graphics_textLeft (g, true, my costFunctionType == FFNet_COST_MSE ? U"Minimum squared error" : U"Minimum cross entropy");
  630. Graphics_marksLeft (g, 2, true, true, false);
  631. Graphics_textBottom (g, true, U"Number of epochs");
  632. Graphics_marksBottom (g, 2, true, true, false);
  633. }
  634. }
  635. autoCollection FFNet_createIrisExample (integer numberOfHidden1, integer numberOfHidden2) {
  636. try {
  637. autoCollection collection = Collection_create ();
  638. autoCategories uniq = Categories_createWithSequentialNumbers (3);
  639. autoFFNet me = FFNet_create (4, numberOfHidden1, numberOfHidden2, 3, false);
  640. FFNet_setOutputCategories (me.get(), uniq.get());
  641. autostring32 name = FFNet_createNameFromTopology (me.get());
  642. Thing_setName (me.get(), name.get());
  643. collection -> addItem_move (me.move());
  644. autoTableOfReal iris = TableOfReal_createIrisDataset ();
  645. /*
  646. Scale data to interval [0-1]
  647. */
  648. for (integer i = 1; i <= 150; i ++) {
  649. for (integer j = 1; j <= 4; j ++) {
  650. iris -> data [i] [j] /= 10.0;
  651. }
  652. }
  653. autoPatternList ap;
  654. autoCategories ac;
  655. TableOfReal_to_PatternList_and_Categories (iris.get(), 0, 0, 0, 0, & ap, & ac);
  656. Thing_setName (ap.get(), U"iris");
  657. Thing_setName (ac.get(), U"iris");
  658. collection -> addItem_move (ap.move());
  659. collection -> addItem_move (ac.move());
  660. return collection;
  661. } catch (MelderError) {
  662. Melder_throw (U"Iris example not created.");
  663. }
  664. }
  665. autoTableOfReal FFNet_extractWeights (FFNet me, integer layer) {
  666. try {
  667. Melder_require (layer > 0 && layer <= my nLayers, U"Layer number should be between 1 and ", my nLayers, U".");
  668. integer numberOfUnitsFrom = my nUnitsInLayer [layer - 1] + 1;
  669. integer numberOfUnitsTo = my nUnitsInLayer [layer];
  670. autoTableOfReal thee = TableOfReal_create (numberOfUnitsFrom, numberOfUnitsTo);
  671. char32 label [40];
  672. for (integer i = 1; i <= numberOfUnitsFrom - 1; i ++) {
  673. Melder_sprint (label,40, U"L", layer - 1, U"-", i);
  674. TableOfReal_setRowLabel (thee.get(), i, label);
  675. }
  676. TableOfReal_setRowLabel (thee.get(), numberOfUnitsFrom, U"Bias");
  677. for (integer i = 1; i <= numberOfUnitsTo; i ++) {
  678. Melder_sprint (label,40, U"L", layer, U"-", i);
  679. TableOfReal_setColumnLabel (thee.get(), i, label);
  680. }
  681. integer node = 1;
  682. for (integer i = 0; i < layer; i ++) {
  683. node += my nUnitsInLayer [i] + 1;
  684. }
  685. for (integer i = 1; i <= numberOfUnitsTo; i ++, node ++) {
  686. integer k = 1;
  687. for (integer j = my wFirst [node]; j <= my wLast [node]; j ++) {
  688. thy data [k ++] [i] = my w [j];
  689. }
  690. }
  691. return thee;
  692. } catch (MelderError) {
  693. Melder_throw (me, U": no TableOfReal created.");
  694. }
  695. }
  696. autoFFNet PatternList_Categories_to_FFNet (PatternList me, Categories you, integer numberOfUnits1, integer numberOfUnits2) {
  697. try {
  698. numberOfUnits1 = numberOfUnits1 > 0 ? numberOfUnits1 : 0;
  699. numberOfUnits2 = numberOfUnits2 > 0 ? numberOfUnits2 : 0;
  700. autoCategories uniq = Categories_selectUniqueItems (you);
  701. integer numberOfOutputs = uniq -> size;
  702. Melder_require (numberOfOutputs > 0, U"The Categories should not be empty.");
  703. autoFFNet result = FFNet_create (my nx, numberOfUnits1, numberOfUnits2, numberOfOutputs, false);
  704. FFNet_setOutputCategories (result.get(), uniq.get());
  705. autostring32 ffnetName = FFNet_createNameFromTopology (result.get());
  706. Thing_setName (result.get(), ffnetName.get());
  707. return result;
  708. } catch (MelderError) {
  709. Melder_throw (me, you, U": no FFNet created.");
  710. }
  711. }
  712. /* End of file FFNet.cpp */