n2p2 - A neural network potential package
NeuralNetwork.cpp
Go to the documentation of this file.
1// n2p2 - A neural network potential package
2// Copyright (C) 2018 Andreas Singraber (University of Vienna)
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17#include "NeuralNetwork.h"
18#include "utility.h"
19#include <algorithm> // std::min, std::max
20#include <cmath> // sqrt, pow, tanh
21#include <cstdio> // fprintf, stderr
22#include <cstdlib> // exit, EXIT_FAILURE, rand, srand
23#include <limits> // std::numeric_limits
24
25#define EXP_LIMIT 35.0
26
27using namespace std;
28using namespace nnp;
29
30NeuralNetwork::
31NeuralNetwork(int numLayers,
32 int const* const& numNeuronsPerLayer,
33 ActivationFunction const* const& activationFunctionsPerLayer)
34{
35 // check number of layers
36 this->numLayers = numLayers;
37 if (numLayers < 3)
38 {
39 fprintf(stderr,
40 "ERROR: Neural network must have at least three layers");
41 exit(EXIT_FAILURE);
42 }
44
45 // do not normalize neurons by default
46 normalizeNeurons = false;
47
48 // allocate layers and populate with neurons
49 layers = new Layer[numLayers];
50 inputLayer = &layers[0];
53 0,
54 numNeuronsPerLayer[0],
55 activationFunctionsPerLayer[0]);
56 for (int i = 1; i < numLayers; i++)
57 {
59 numNeuronsPerLayer[i-1],
60 numNeuronsPerLayer[i],
61 activationFunctionsPerLayer[i]);
62 }
63
64 // count connections
65 numWeights = 0;
66 numBiases = 0;
68 for (int i = 1; i < numLayers; i++)
69 {
72 }
74
75 // calculate weight and bias offsets for each layer
76 weightOffset = new int[numLayers-1];
77 weightOffset[0] = 0;
78 for (int i = 1; i < numLayers-1; i++)
79 {
80 weightOffset[i] = weightOffset[i-1] +
81 (layers[i-1].numNeurons + 1) * layers[i].numNeurons;
82 }
83 biasOffset = new int[numLayers-1];
84 for (int i = 0; i < numLayers-1; i++)
85 {
86 biasOffset[i] = weightOffset[i] +
88 }
89 biasOnlyOffset = new int[numLayers-1];
90 biasOnlyOffset[0] = 0;
91 for (int i = 1; i < numLayers-1; i++)
92 {
94 }
95}
96
98{
99 for (int i = 0; i < numLayers; i++)
100 {
101 for (int j = 0; j < layers[i].numNeurons; j++)
102 {
103 delete[] layers[i].neurons[j].weights;
104 }
105 delete[] layers[i].neurons;
106 }
107 delete[] layers;
108 delete[] weightOffset;
109 delete[] biasOffset;
110 delete[] biasOnlyOffset;
111}
112
113void NeuralNetwork::setNormalizeNeurons(bool normalizeNeurons)
114{
115 this->normalizeNeurons = normalizeNeurons;
116
117 return;
118}
119
121{
122 int count = 0;
123
124 for (int i = 0; i < numLayers; i++)
125 {
126 count += layers[i].numNeurons;
127 }
128
129 return count;
130}
131
133{
134 return numConnections;
135}
136
138{
139 return numWeights;
140}
141
143{
144 return numBiases;
145}
146
147void NeuralNetwork::setConnections(double const* const& connections)
148{
149 int count = 0;
150
151 for (int i = 1; i < numLayers; i++)
152 {
153 for (int j = 0; j < layers[i].numNeuronsPrevLayer; j++)
154 {
155 for (int k = 0; k < layers[i].numNeurons; k++)
156 {
157 layers[i].neurons[k].weights[j] = connections[count];
158 count++;
159 }
160 }
161 for (int j = 0; j < layers[i].numNeurons; j++)
162 {
163 layers[i].neurons[j].bias = connections[count];
164 count++;
165 }
166 }
167
168 return;
169}
170
171void NeuralNetwork::getConnections(double* connections) const
172{
173 int count = 0;
174
175 for (int i = 1; i < numLayers; i++)
176 {
177 for (int j = 0; j < layers[i].numNeuronsPrevLayer; j++)
178 {
179 for (int k = 0; k < layers[i].numNeurons; k++)
180 {
181 connections[count] = layers[i].neurons[k].weights[j] ;
182 count++;
183 }
184 }
185 for (int j = 0; j < layers[i].numNeurons; j++)
186 {
187 connections[count] = layers[i].neurons[j].bias;
188 count++;
189 }
190 }
191
192 return;
193}
194
196{
197 double* connections = new double[numConnections];
198
199 srand(seed);
200 for (int i = 0; i < numConnections; i++)
201 {
202 connections[i] = -1.0 + 2.0 * (double)rand() / RAND_MAX;
203 }
204
205 setConnections(connections);
206
207 delete[] connections;
208
209 return;
210}
211
213{
214 if (modificationScheme == MS_ZEROBIAS)
215 {
216 for (int i = 0; i < numLayers; i++)
217 {
218 for (int j = 0; j < layers[i].numNeurons; j++)
219 {
220 layers[i].neurons[j].bias = 0.0;
221 }
222 }
223 }
224 else if (modificationScheme == MS_ZEROOUTPUTWEIGHTS)
225 {
226 for (int i = 0; i < outputLayer->numNeurons; i++)
227 {
228 for (int j = 0; j < outputLayer->numNeuronsPrevLayer; j++)
229 {
230 outputLayer->neurons[i].weights[j] = 0.0;
231 }
232 }
233 }
234 else if (modificationScheme == MS_FANIN)
235 {
236 for (int i = 1; i < numLayers; i++)
237 {
238 if(layers[i].activationFunction == AF_TANH)
239 {
240 for (int j = 0; j < layers[i].numNeurons; j++)
241 {
242 for (int k = 0; k < layers[i].numNeuronsPrevLayer; k++)
243 {
244 layers[i].neurons[j].weights[k] /=
245 sqrt(layers[i].numNeuronsPrevLayer);
246 }
247 }
248 }
249 }
250 }
251 else if (modificationScheme == MS_GLOROTBENGIO)
252 {
253 for (int i = 1; i < numLayers; i++)
254 {
255 if(layers[i].activationFunction == AF_TANH)
256 {
257 for (int j = 0; j < layers[i].numNeurons; j++)
258 {
259 for (int k = 0; k < layers[i].numNeuronsPrevLayer; k++)
260 {
261 layers[i].neurons[j].weights[k] *= sqrt(6.0 / (
262 layers[i].numNeuronsPrevLayer
263 + layers[i].numNeurons));
264 }
265 }
266 }
267 }
268 }
269 else if (modificationScheme == MS_NGUYENWIDROW)
270 {
271 double beta = 0.0;
272 double sum = 0.0;
273 double weight = 0.0;
274
275 for (int i = 1; i < numLayers-1; i++)
276 {
277 beta = 0.7 * pow(layers[i].numNeurons,
278 1.0 / double(layers[i].numNeuronsPrevLayer));
279 for (int j = 0; j < layers[i].numNeurons; j++)
280 {
281 sum = 0.0;
282 for (int k = 0; k < layers[i].numNeuronsPrevLayer; k++)
283 {
284 weight = layers[i].neurons[j].weights[k];
285 sum += weight * weight;
286 }
287 sum = sqrt(sum);
288 for (int k = 0; k < layers[i].numNeuronsPrevLayer; k++)
289 {
290 layers[i].neurons[j].weights[k] *= beta / sum;
291 if (layers[i].activationFunction == AF_TANH)
292 {
293 layers[i].neurons[j].weights[k] *= 2.0;
294 }
295 }
296 layers[i].neurons[j].bias *= beta;
297 if (layers[i].activationFunction == AF_TANH)
298 {
299 layers[i].neurons[j].bias *= 2.0;
300 }
301 }
302 }
303 for (int i = 0; i < outputLayer->numNeurons; i++)
304 {
305 outputLayer->neurons[0].weights[i] *= 0.5;
306 }
307 }
308 else
309 {
310 fprintf(stderr, "ERROR: Incorrect modifyConnections call.\n");
311 exit(EXIT_FAILURE);
312 }
313
314 return;
315}
316
318 double parameter1,
319 double parameter2)
320{
321 if (modificationScheme == MS_PRECONDITIONOUTPUT)
322 {
323 double mean = parameter1;
324 double sigma = parameter2;
325
326 for (int i = 0; i < outputLayer->numNeurons; i++)
327 {
328 for (int j = 0; j < outputLayer->numNeuronsPrevLayer; j++)
329 {
330 outputLayer->neurons[i].weights[j] *= sigma;
331 }
332 outputLayer->neurons[i].bias += mean;
333 }
334 }
335 else
336 {
337 fprintf(stderr, "ERROR: Incorrect modifyConnections call.\n");
338 exit(EXIT_FAILURE);
339 }
340
341 return;
342}
343
344void NeuralNetwork::setInput(size_t const index, double const value) const
345{
346 Neuron& n = inputLayer->neurons[index];
347 n.count++;
348 n.value = value;
349 n.min = min(value, n.min);
350 n.max = max(value, n.max);
351 n.sum += value;
352 n.sum2 += value * value;
353
354 return;
355}
356
357void NeuralNetwork::setInput(double const* const& input) const
358{
359 for (int i = 0; i < inputLayer->numNeurons; i++)
360 {
361 double const& value = input[i];
362 Neuron& n = inputLayer->neurons[i];
363 n.count++;
364 n.value = value;
365 n.min = min(value, n.min);
366 n.max = max(value, n.max);
367 n.sum += value;
368 n.sum2 += value * value;
369 }
370
371 return;
372}
373
374void NeuralNetwork::getOutput(double* output) const
375{
376 for (int i = 0; i < outputLayer->numNeurons; i++)
377 {
378 output[i] = outputLayer->neurons[i].value;
379 }
380
381 return;
382}
383
385{
386 for (int i = 1; i < numLayers; i++)
387 {
388 propagateLayer(layers[i], layers[i-1]);
389 }
390
391 return;
392}
393
394void NeuralNetwork::calculateDEdG(double *dEdG) const
395{
396 double** inner = new double*[numHiddenLayers];
397 double** outer = new double*[numHiddenLayers];
398
399 for (int i = 0; i < numHiddenLayers; i++)
400 {
401 inner[i] = new double[layers[i+1].numNeurons];
402 outer[i] = new double[layers[i+2].numNeurons];
403 }
404
405 for (int k = 0; k < layers[0].numNeurons; k++)
406 {
407 for (int i = 0; i < layers[1].numNeurons; i++)
408 {
409 inner[0][i] = layers[1].neurons[i].weights[k]
410 * layers[1].neurons[i].dfdx;
411 if (normalizeNeurons) inner[0][i] /= layers[0].numNeurons;
412 }
413 for (int l = 1; l < numHiddenLayers+1; l++)
414 {
415 for (int i2 = 0; i2 < layers[l+1].numNeurons; i2++)
416 {
417 outer[l-1][i2] = 0.0;
418 for (int i1 = 0; i1 < layers[l].numNeurons; i1++)
419 {
420 outer[l-1][i2] += layers[l+1].neurons[i2].weights[i1]
421 * inner[l-1][i1];
422 }
423 outer[l-1][i2] *= layers[l+1].neurons[i2].dfdx;
424 if (normalizeNeurons) outer[l-1][i2] /= layers[l].numNeurons;
425 if (l < numHiddenLayers) inner[l][i2] = outer[l-1][i2];
426 }
427 }
428 dEdG[k] = outer[numHiddenLayers-1][0];
429 }
430
431 for (int i = 0; i < numHiddenLayers; i++)
432 {
433 delete[] inner[i];
434 delete[] outer[i];
435 }
436 delete[] inner;
437 delete[] outer;
438
439 return;
440}
441
442void NeuralNetwork::calculateDEdc(double* dEdc) const
443{
444 int count = 0;
445
446 for (int i = 0; i < numConnections; i++)
447 {
448 dEdc[i] = 0.0;
449 }
450
451 for (int i = 0; i < outputLayer->numNeurons; i++)
452 {
455 {
456 dEdc[biasOffset[numLayers-2]+i] /=
458 }
459 }
460
461 for (int i = numLayers-2; i >= 0; i--)
462 {
463 count = 0;
464 for (int j = 0; j < layers[i].numNeurons; j++)
465 {
466 for (int k = 0; k < layers[i+1].numNeurons; k++)
467 {
468 dEdc[weightOffset[i]+count] = dEdc[biasOffset[i]+k]
469 * layers[i].neurons[j].value;
470 count++;
471 if (i >= 1)
472 {
473 dEdc[biasOffset[i-1]+j] += dEdc[biasOffset[i]+k]
474 * layers[i+1].neurons[k].weights[j]
475 * layers[i].neurons[j].dfdx;
476 }
477 }
478 if (normalizeNeurons && i >= 1)
479 {
480 dEdc[biasOffset[i-1]+j] /= layers[i].numNeuronsPrevLayer;
481 }
482 }
483 }
484
485 return;
486}
487
489 double const* const& dGdxyz) const
490{
491 double* dEdb = new double[numBiases];
492 double* d2EdGdc = new double[numConnections];
493
494 for (int i = 0; i < numBiases; i++)
495 {
496 dEdb[i] = 0.0;
497 }
498 for (int i = 0; i < numConnections; i++)
499 {
500 dFdc[i] = 0.0;
501 d2EdGdc[i] = 0.0;
502 }
503
504 calculateDEdb(dEdb);
505 for (int i = 0; i < layers[0].numNeurons; i++)
506 {
507 for (int j = 0; j < numConnections; j++)
508 {
509 d2EdGdc[j] = 0.0;
510 }
511 calculateDxdG(i);
512 calculateD2EdGdc(i, dEdb, d2EdGdc);
513 for (int j = 0; j < numConnections; j++)
514 {
515 // Note: F = - dE / dx !!
516 // ^
517 dFdc[j] -= d2EdGdc[j] * dGdxyz[i];
518 }
519 }
520
521 delete[] dEdb;
522 delete[] d2EdGdc;
523
524 return;
525}
526
527void NeuralNetwork::writeConnections(std::ofstream& file) const
528{
529 // File header.
530 vector<string> title;
531 vector<string> colName;
532 vector<string> colInfo;
533 vector<size_t> colSize;
534 title.push_back("Neural network connection values (weights and biases).");
535 colSize.push_back(24);
536 colName.push_back("connection");
537 colInfo.push_back("Neural network connection value.");
538 colSize.push_back(1);
539 colName.push_back("t");
540 colInfo.push_back("Connection type (a = weight, b = bias).");
541 colSize.push_back(9);
542 colName.push_back("index");
543 colInfo.push_back("Index enumerating weights.");
544 colSize.push_back(5);
545 colName.push_back("l_s");
546 colInfo.push_back("Starting point layer (end point layer for biases).");
547 colSize.push_back(5);
548 colName.push_back("n_s");
549 colInfo.push_back("Starting point neuron in starting layer (end point "
550 "neuron for biases).");
551 colSize.push_back(5);
552 colName.push_back("l_e");
553 colInfo.push_back("End point layer.");
554 colSize.push_back(5);
555 colName.push_back("n_e");
556 colInfo.push_back("End point neuron in end layer.");
558 createFileHeader(title, colSize, colName, colInfo));
559
560 int count = 0;
561 for (int i = 1; i < numLayers; i++)
562 {
563 for (int j = 0; j < layers[i].numNeuronsPrevLayer; j++)
564 {
565 for (int k = 0; k < layers[i].numNeurons; k++)
566 {
567 count++;
568 file << strpr("%24.16E a %9d %5d %5d %5d %5d\n",
569 layers[i].neurons[k].weights[j],
570 count,
571 i - 1,
572 j + 1,
573 i,
574 k + 1);
575 }
576 }
577 for (int j = 0; j < layers[i].numNeurons; j++)
578 {
579 count++;
580 file << strpr("%24.16E b %9d %5d %5d\n",
581 layers[i].neurons[j].bias,
582 count,
583 i,
584 j + 1);
585 }
586 }
587
588 return;
589}
590
591void NeuralNetwork::calculateDEdb(double* dEdb) const
592{
593 for (int i = 0; i < outputLayer->numNeurons; i++)
594 {
597 {
598 dEdb[biasOnlyOffset[numLayers-2]+i] /=
600 }
601 }
602
603 for (int i = numLayers-2; i >= 0; i--)
604 {
605 for (int j = 0; j < layers[i].numNeurons; j++)
606 {
607 for (int k = 0; k < layers[i+1].numNeurons; k++)
608 {
609 if (i >= 1)
610 {
611 dEdb[biasOnlyOffset[i-1]+j] += dEdb[biasOnlyOffset[i]+k]
612 * layers[i+1].neurons[k].weights[j]
613 * layers[i].neurons[j].dfdx;
614 }
615 }
616 if (normalizeNeurons && i >= 1)
617 {
618 dEdb[biasOnlyOffset[i-1]+j] /= layers[i].numNeuronsPrevLayer;
619 }
620 }
621 }
622
623 return;
624}
625
626void NeuralNetwork::calculateDxdG(int index) const
627{
628 for (int i = 0; i < layers[1].numNeurons; i++)
629 {
630 layers[1].neurons[i].dxdG = layers[1].neurons[i].weights[index];
632 {
634 }
635 }
636 for (int i = 2; i < numLayers; i++)
637 {
638 for (int j = 0; j < layers[i].numNeurons; j++)
639 {
640 layers[i].neurons[j].dxdG = 0.0;
641 for (int k = 0; k < layers[i-1].numNeurons; k++)
642 {
643 layers[i].neurons[j].dxdG += layers[i].neurons[j].weights[k]
644 * layers[i-1].neurons[k].dfdx
645 * layers[i-1].neurons[k].dxdG;
646 }
648 {
650 }
651 }
652 }
653
654 return;
655}
656
658 double const* const& dEdb,
659 double* d2EdGdc) const
660{
661 int count = 0;
662
663 for (int i = 0; i < outputLayer->numNeurons; i++)
664 {
665 d2EdGdc[biasOffset[numLayers-2]+i] = outputLayer->neurons[i].d2fdx2
668 {
669 d2EdGdc[biasOffset[numLayers-2]+i] /=
671 }
672 }
673
674 for (int i = numLayers-2; i >= 0; i--)
675 {
676 count = 0;
677 for (int j = 0; j < layers[i].numNeurons; j++)
678 {
679 for (int k = 0; k < layers[i+1].numNeurons; k++)
680 {
681 if (i == 0)
682 {
683 d2EdGdc[weightOffset[i]+count] =
684 d2EdGdc[biasOffset[i]+k] * layers[i].neurons[j].value;
685 if (j == index)
686 {
687 d2EdGdc[weightOffset[i]+count] +=
688 dEdb[biasOnlyOffset[i]+k];
689 }
690 }
691 else
692 {
693 d2EdGdc[weightOffset[i]+count] =
694 d2EdGdc[biasOffset[i]+k] * layers[i].neurons[j].value
695 + dEdb[biasOnlyOffset[i]+k] * layers[i].neurons[j].dfdx
696 * layers[i].neurons[j].dxdG;
697 }
698 count++;
699 if (i >= 1)
700 {
701 d2EdGdc[biasOffset[i-1]+j] +=
702 layers[i+1].neurons[k].weights[j]
703 * (d2EdGdc[biasOffset[i]+k] * layers[i].neurons[j].dfdx
704 + dEdb[biasOnlyOffset[i]+k]
705 * layers[i].neurons[j].d2fdx2
706 * layers[i].neurons[j].dxdG);
707 }
708 }
709 if (normalizeNeurons && i >= 1)
710 {
711 d2EdGdc[biasOffset[i-1]+j] /= layers[i].numNeuronsPrevLayer;
712 }
713 }
714 }
715
716 return;
717}
718
720 int numNeuronsPrevLayer,
721 int numNeurons,
722 ActivationFunction activationFunction)
723{
724 layer.numNeurons = numNeurons;
725 layer.numNeuronsPrevLayer = numNeuronsPrevLayer;
726 layer.activationFunction = activationFunction;
727
728 layer.neurons = new Neuron[layer.numNeurons];
729 for (int i = 0; i < layer.numNeurons; i++)
730 {
731 layer.neurons[i].x = 0.0;
732 layer.neurons[i].value = 0.0;
733 layer.neurons[i].dfdx = 0.0;
734 layer.neurons[i].d2fdx2 = 0.0;
735 layer.neurons[i].bias = 0.0;
736 layer.neurons[i].dxdG = 0.0;
737 layer.neurons[i].count = 0;
738 layer.neurons[i].min = numeric_limits<double>::max();
739 layer.neurons[i].max = -numeric_limits<double>::max();
740 layer.neurons[i].sum = 0.0;
741 layer.neurons[i].sum2 = 0.0;
742 if (layer.numNeuronsPrevLayer > 0)
743 {
744 layer.neurons[i].weights = new double[layer.numNeuronsPrevLayer];
745 for (int j = 0; j < layer.numNeuronsPrevLayer; j++)
746 {
747 layer.neurons[i].weights[j] = 0.0;
748 }
749 }
750 else
751 {
752 layer.neurons[i].weights = 0;
753 }
754 }
755
756 return;
757}
758
760{
761 double dtmp = 0.0;
762
763 for (int i = 0; i < layer.numNeurons; i++)
764 {
765 dtmp = 0.0;
766 for (int j = 0; j < layer.numNeuronsPrevLayer; j++)
767 {
768 dtmp += layer.neurons[i].weights[j] * layerPrev.neurons[j].value;
769 }
770 dtmp += layer.neurons[i].bias;
772 {
773 dtmp /= layer.numNeuronsPrevLayer;
774 }
775
776 layer.neurons[i].x = dtmp;
777 if (layer.activationFunction == AF_IDENTITY)
778 {
779 layer.neurons[i].value = dtmp;
780 layer.neurons[i].dfdx = 1.0;
781 layer.neurons[i].d2fdx2 = 0.0;
782 }
783 else if (layer.activationFunction == AF_TANH)
784 {
785 dtmp = tanh(dtmp);
786 layer.neurons[i].value = dtmp;
787 layer.neurons[i].dfdx = 1.0 - dtmp * dtmp;
788 layer.neurons[i].d2fdx2 = -2.0 * dtmp * (1.0 - dtmp * dtmp);
789 }
790 else if (layer.activationFunction == AF_LOGISTIC)
791 {
792 if (dtmp > EXP_LIMIT)
793 {
794 layer.neurons[i].value = 1.0;
795 layer.neurons[i].dfdx = 0.0;
796 layer.neurons[i].d2fdx2 = 0.0;
797 }
798 else if (dtmp < -EXP_LIMIT)
799 {
800 layer.neurons[i].value = 0.0;
801 layer.neurons[i].dfdx = 0.0;
802 layer.neurons[i].d2fdx2 = 0.0;
803 }
804 else
805 {
806 dtmp = 1.0 / (1.0 + exp(-dtmp));
807 layer.neurons[i].value = dtmp;
808 layer.neurons[i].dfdx = dtmp * (1.0 - dtmp);
809 layer.neurons[i].d2fdx2 = dtmp * (1.0 - dtmp)
810 * (1.0 - 2.0 * dtmp);
811 }
812 }
813 else if (layer.activationFunction == AF_SOFTPLUS)
814 {
815 if (dtmp > EXP_LIMIT)
816 {
817 layer.neurons[i].value = dtmp;
818 layer.neurons[i].dfdx = 1.0;
819 layer.neurons[i].d2fdx2 = 0.0;
820 }
821 else if (dtmp < -EXP_LIMIT)
822 {
823 layer.neurons[i].value = 0.0;
824 layer.neurons[i].dfdx = 0.0;
825 layer.neurons[i].d2fdx2 = 0.0;
826 }
827 else
828 {
829 dtmp = exp(dtmp);
830 layer.neurons[i].value = log(1.0 + dtmp);
831 dtmp = 1.0 / (1.0 + 1.0 / dtmp);
832 layer.neurons[i].dfdx = dtmp;
833 layer.neurons[i].d2fdx2 = dtmp * (1.0 - dtmp);
834 }
835 }
836 else if (layer.activationFunction == AF_RELU)
837 {
838 if (dtmp > 0.0)
839 {
840 layer.neurons[i].value = dtmp;
841 layer.neurons[i].dfdx = 1.0;
842 layer.neurons[i].d2fdx2 = 0.0;
843 }
844 else
845 {
846 layer.neurons[i].value = 0.0;
847 layer.neurons[i].dfdx = 0.0;
848 layer.neurons[i].d2fdx2 = 0.0;
849 }
850 }
851 else if (layer.activationFunction == AF_GAUSSIAN)
852 {
853 double const tmpexp = exp(-0.5 * dtmp * dtmp);
854 layer.neurons[i].value = tmpexp;
855 layer.neurons[i].dfdx = -dtmp * tmpexp;
856 layer.neurons[i].d2fdx2 = (dtmp * dtmp - 1.0) * tmpexp;
857 }
858 else if (layer.activationFunction == AF_COS)
859 {
860 double const tmpcos = cos(dtmp);
861 layer.neurons[i].value = tmpcos;
862 layer.neurons[i].dfdx = -sin(dtmp);
863 layer.neurons[i].d2fdx2 = -tmpcos;
864 }
865 else if (layer.activationFunction == AF_REVLOGISTIC)
866 {
867 dtmp = 1.0 / (1.0 + exp(-dtmp));
868 layer.neurons[i].value = 1.0 - dtmp;
869 layer.neurons[i].dfdx = dtmp * (dtmp - 1.0);
870 layer.neurons[i].d2fdx2 = dtmp * (dtmp - 1.0) * (1.0 - 2.0 * dtmp);
871 }
872 else if (layer.activationFunction == AF_EXP)
873 {
874 dtmp = exp(-dtmp);
875 layer.neurons[i].value = dtmp;
876 layer.neurons[i].dfdx = -dtmp;
877 layer.neurons[i].d2fdx2 = dtmp;
878 }
879 else if (layer.activationFunction == AF_HARMONIC)
880 {
881 layer.neurons[i].value = dtmp * dtmp;
882 layer.neurons[i].dfdx = 2.0 * dtmp;
883 layer.neurons[i].d2fdx2 = 2.0;
884 }
885 layer.neurons[i].count++;
886 dtmp = layer.neurons[i].x;
887 layer.neurons[i].min = min(dtmp, layer.neurons[i].min);
888 layer.neurons[i].max = max(dtmp, layer.neurons[i].max);
889 layer.neurons[i].sum += dtmp;
890 layer.neurons[i].sum2 += dtmp * dtmp;
891 }
892
893 return;
894}
895
897{
898 for (int i = 0; i < numLayers; i++)
899 {
900 for (int j = 0; j < layers[i].numNeurons; j++)
901 {
902 layers[i].neurons[j].count = 0;
903 layers[i].neurons[j].min = numeric_limits<double>::max();
904 layers[i].neurons[j].max = -numeric_limits<double>::max();
905 layers[i].neurons[j].sum = 0.0;
906 layers[i].neurons[j].sum2 = 0.0;
907 }
908 }
909
910 return;
911}
912
914 double* min,
915 double* max,
916 double* sum,
917 double* sum2) const
918{
919 int iNeuron = 0;
920
921 for (int i = 0; i < numLayers; i++)
922 {
923 for (int j = 0; j < layers[i].numNeurons; j++)
924 {
925 count[iNeuron] = layers[i].neurons[j].count;
926 min [iNeuron] = layers[i].neurons[j].min;
927 max [iNeuron] = layers[i].neurons[j].max;
928 sum [iNeuron] = layers[i].neurons[j].sum;
929 sum2 [iNeuron] = layers[i].neurons[j].sum2;
930 iNeuron++;
931 }
932 }
933
934 return;
935}
936
937/*
938void NeuralNetwork::writeStatus(int element, int epoch)
939{
940 char fName[LSTR] = "";
941 FILE* fpn = NULL;
942 FILE* fpw = NULL;
943
944 for (int i = 0; i < numLayers; i++)
945 {
946 sprintf(fName, "nn.neurons.%03d.%1d.%06d", element, i, epoch);
947 fpn = fopen(fName, "a");
948 if (fpn == NULL)
949 {
950 fprintf(stderr, "ERROR: Could not open file: %s.\n", fName);
951 exit(EXIT_FAILURE);
952 }
953 sprintf(fName, "nn.weights.%03d.%1d.%06d", element, i, epoch);
954 fpw = fopen(fName, "a");
955 if (fpw == NULL)
956 {
957 fprintf(stderr, "ERROR: Could not open file: %s.\n", fName);
958 exit(EXIT_FAILURE);
959 }
960 for (int j = 0; j < layers[i].numNeurons; j++)
961 {
962 fprintf(fpn, "%4d %.8f %.8f %.8f %.8f %.8f %.8f\n", j, layers[i].neurons[j].x,
963 layers[i].neurons[j].value, layers[i].neurons[j].dfdx, layers[i].neurons[j].d2fdx2,
964 layers[i].neurons[j].bias, layers[i].neurons[j].dxdG);
965 for (int k = 0; k < layers[i].numNeuronsPrevLayer; k++)
966 {
967 fprintf(fpw, "%4d %4d %.8f\n", j, k, layers[i].neurons[j].weights[k]);
968 }
969 }
970 fclose(fpn);
971 fclose(fpw);
972 }
973
974 return;
975
976}
977*/
978
980{
981 long mem = sizeof(*this);
982 int numNeurons = getNumNeurons();
983
984 mem += (numLayers - 1) * sizeof(int); // weightOffset
985 mem += (numLayers - 1) * sizeof(int); // biasOffset
986 mem += (numLayers - 1) * sizeof(int); // biasOnlyOffset
987 mem += numLayers * sizeof(Layer); // layers
988 mem += numNeurons * sizeof(Neuron); // neurons
989 mem += numWeights * sizeof(double); // weights
990
991 return mem;
992}
993
994vector<string> NeuralNetwork::info() const
995{
996 vector<string> v;
997 int maxNeurons = 0;
998
999 v.push_back(strpr("Number of weights : %6zu\n", numWeights));
1000 v.push_back(strpr("Number of biases : %6zu\n", numBiases));
1001 v.push_back(strpr("Number of connections: %6zu\n", numConnections));
1002 v.push_back(strpr("Architecture "));
1003 for (int i = 0; i < numLayers; ++i)
1004 {
1005 maxNeurons = max(layers[i].numNeurons, maxNeurons);
1006 v.push_back(strpr(" %4d", layers[i].numNeurons));
1007 }
1008 v.push_back("\n");
1009 v.push_back("-----------------------------------------"
1010 "--------------------------------------\n");
1011
1012 for (int i = 0; i < maxNeurons; ++i)
1013 {
1014 v.push_back(strpr("%4d", i + 1));
1015 string s = "";
1016 for (int j = 0; j < numLayers; ++j)
1017 {
1018 if (i < layers[j].numNeurons)
1019 {
1020 if (j == 0)
1021 {
1022 s += strpr(" %3s", "G");
1023 }
1024 else if (layers[j].activationFunction == AF_IDENTITY)
1025 {
1026 s += strpr(" %3s", "l");
1027 }
1028 else if (layers[j].activationFunction == AF_TANH)
1029 {
1030 s += strpr(" %3s", "t");
1031 }
1032 else if (layers[j].activationFunction == AF_LOGISTIC)
1033 {
1034 s += strpr(" %3s", "s");
1035 }
1036 else if (layers[j].activationFunction == AF_SOFTPLUS)
1037 {
1038 s += strpr(" %3s", "p");
1039 }
1040 else if (layers[j].activationFunction == AF_RELU)
1041 {
1042 s += strpr(" %3s", "r");
1043 }
1044 else if (layers[j].activationFunction == AF_GAUSSIAN)
1045 {
1046 s += strpr(" %3s", "g");
1047 }
1048 else if (layers[j].activationFunction == AF_COS)
1049 {
1050 s += strpr(" %3s", "c");
1051 }
1052 else if (layers[j].activationFunction == AF_REVLOGISTIC)
1053 {
1054 s += strpr(" %3s", "S");
1055 }
1056 else if (layers[j].activationFunction == AF_EXP)
1057 {
1058 s += strpr(" %3s", "e");
1059 }
1060 else if (layers[j].activationFunction == AF_HARMONIC)
1061 {
1062 s += strpr(" %3s", "h");
1063 }
1064 }
1065 else
1066 {
1067 s += " ";
1068 }
1069 }
1070 v.push_back(s += "\n");
1071 }
1072
1073 return v;
1074}
#define EXP_LIMIT
ActivationFunction
List of available activation function types.
Definition: NeuralNetwork.h:33
@ AF_RELU
(NOT recommended for HDNNPs!)
Definition: NeuralNetwork.h:43
int getNumConnections() const
Return total number of connections.
Layer * inputLayer
Pointer to input layer.
void setInput(double const *const &input) const
Set neural network input layer node values.
int getNumNeurons() const
Return total number of neurons.
int * biasOnlyOffset
Offset adress of biases per layer in bias only array.
void modifyConnections(ModificationScheme modificationScheme)
Change connections according to a given modification scheme.
int numLayers
Total number of layers (includes input and output layers).
void calculateDEdb(double *dEdb) const
Calculate derivative of output neuron with respect to biases.
int getNumWeights() const
Return number of weights.
void setConnections(double const *const &connections)
Set neural network weights and biases.
void writeConnections(std::ofstream &file) const
Write connections to file.
bool normalizeNeurons
If neurons are normalized.
void calculateDFdc(double *dFdc, double const *const &dGdxyz) const
Calculate "second" derivative of output with respect to connections.
void propagateLayer(Layer &layer, Layer &layerPrev)
Propagate information from one layer to the next.
void allocateLayer(Layer &layer, int numNeuronsPrevLayer, int numNeurons, ActivationFunction activationFunction)
Allocate a single layer.
int * weightOffset
Offset adress of weights per layer in combined weights+bias array.
void initializeConnectionsRandomUniform(unsigned int seed)
Initialize connections with random numbers.
int * biasOffset
Offset adress of biases per layer in combined weights+bias array.
void calculateD2EdGdc(int index, double const *const &dEdb, double *d2EdGdc) const
Calculate second derivative of output neuron with respect to input neuron and connections.
void getNeuronStatistics(long *count, double *min, double *max, double *sum, double *sum2) const
Return gathered neuron statistics.
int numBiases
Number of NN biases only.
void resetNeuronStatistics()
Reset neuron statistics.
ModificationScheme
List of available connection modification schemes.
Definition: NeuralNetwork.h:58
@ MS_ZEROOUTPUTWEIGHTS
Set all weights connecting to the output layer to zero.
Definition: NeuralNetwork.h:62
@ MS_ZEROBIAS
Set all bias values to zero.
Definition: NeuralNetwork.h:60
@ MS_PRECONDITIONOUTPUT
Apply preconditioning to output layer connections.
@ MS_FANIN
Normalize weights via number of neuron inputs (fan-in).
Definition: NeuralNetwork.h:72
@ MS_GLOROTBENGIO
Normalize connections according to Glorot and Bengio.
Definition: NeuralNetwork.h:88
@ MS_NGUYENWIDROW
Initialize connections according to Nguyen-Widrow scheme.
void getConnections(double *connections) const
Get neural network weights and biases.
int numConnections
Number of NN connections (weights + biases).
void propagate()
Propagate input information through all layers.
void calculateDEdc(double *dEdc) const
Calculate derivative of output neuron with respect to connections.
void setNormalizeNeurons(bool normalizeNeurons)
Turn on/off neuron normalization.
int getNumBiases() const
Return number of biases.
void calculateDEdG(double *dEdG) const
Calculate derivative of output neuron with respect to input neurons.
void calculateDxdG(int index) const
Calculate derivative of neuron values before activation function with respect to input neuron.
Layer * layers
Neural network layers.
int numWeights
Number of NN weights only.
void getOutput(double *output) const
Get neural network output layer node values.
Layer * outputLayer
Pointer to output layer.
int numHiddenLayers
Number of hidden layers.
std::vector< std::string > info() const
Print neural network architecture.
Definition: Atom.h:28
string strpr(const char *format,...)
String version of printf function.
Definition: utility.cpp:90
vector< string > createFileHeader(vector< string > const &title, vector< size_t > const &colSize, vector< string > const &colName, vector< string > const &colInfo, char const &commentChar)
Definition: utility.cpp:104
void appendLinesToFile(ofstream &file, vector< string > const lines)
Append multiple lines of strings to open file stream.
Definition: utility.cpp:218
One neural network layer.
int numNeurons
Number of neurons in this layer .
Neuron * neurons
Array of neurons in this layer.
ActivationFunction activationFunction
Common activation function for all neurons in this layer.
int numNeuronsPrevLayer
Number of neurons in previous layer .
double * weights
NN weights assigned to neuron.
double bias
Bias value assigned to this neuron (if this is neuron this bias value is ).
double max
Maximum neuron value over data set (neuron statistics).
double sum
Sum of neuron values over data set (neuron statistics).
double dxdG
Derivative of neuron value before application of activation function with respect to input layer neur...
double value
Neuron value.
long count
How often the value of this neuron has been evaluated.
double d2fdx2
Second derivative of activation function with respect to its argument .
double dfdx
Derivative of activation function with respect to its argument .
double min
Minimum neuron value over data set (neuron statistics).
double x
Neuron value before application of activation function.
double sum2
Sum of squared neuron values over data set (neuron statistics).