NeuralNet 1.0
Loading...
Searching...
No Matches
Dense.hpp
1#pragma once
2
3#include <cereal/access.hpp>
4#include <cereal/cereal.hpp>
5#include <cereal/types/base_class.hpp>
6#include <cereal/types/polymorphic.hpp>
7
8#include "Layer.hpp"
9
10namespace NeuralNet {
11class Dense : public Layer {
12 public:
13 Dense(int nNeurons, ACTIVATION activation = ACTIVATION::SIGMOID,
14 WEIGHT_INIT weightInit = WEIGHT_INIT::RANDOM, int bias = 0) {
15 type = LayerType::DENSE;
16 this->bias = bias;
17 this->nNeurons = nNeurons;
18 this->weightInit = weightInit;
19 this->activation = activation;
20 this->setActivation(activation);
21 };
22
28 Eigen::MatrixXd getWeights() const { return weights; };
29
35 Eigen::MatrixXd getBiases() const { return biases; };
36
42 Eigen::MatrixXd getOutputs() const { return outputs; };
43
47 void printWeights() {
48 std::cout << this->weights << "\n";
49 return;
50 };
51
55 std::string getSlug() const override {
56 return slug + std::to_string(nNeurons) + activationSlug;
57 }
58
66 virtual Eigen::MatrixXd feedInputs(Eigen::MatrixXd inputs,
67 bool training = false) override {
68 // Dense layer positioned as input layer
69 if (weights.rows() == 0 && weights.cols() == 0) {
70 setOutputs(inputs);
71 return inputs;
72 }
73
74 if (inputs.cols() != weights.rows()) {
75 Eigen::MatrixXd transposedMat = inputs.transpose();
76 inputs = transposedMat;
77 }
78
79 assert(inputs.cols() == weights.rows());
80 return this->computeOutputs(inputs, training);
81 };
82
83 ~Dense(){};
84
85 private:
86 // non-public serialization
87 friend class cereal::access;
88 friend class Network;
89
90 double bias;
91 std::string slug = "dns";
92 std::string activationSlug = "";
93 Eigen::MatrixXd biases;
94 WEIGHT_INIT weightInit;
95 Eigen::MatrixXd weights;
96 Eigen::MatrixXd cachedWeights;
97 Eigen::MatrixXd cachedBiases;
98 ACTIVATION activation;
99 Eigen::MatrixXd (*activate)(const Eigen::MatrixXd &);
100 Eigen::MatrixXd (*diff)(const Eigen::MatrixXd &);
101
102 template <class Archive>
103 void save(Archive &ar) const {
104 ar(cereal::base_class<Layer>(this), nNeurons, biases, weights, activation);
105 }
106
107 template <class Archive>
108 void load(Archive &ar) {
109 ar(cereal::base_class<Layer>(this), nNeurons, biases, weights, activation);
110 setActivation(activation);
111 }
112
118 void init(int numRows) override {
119 // First and foremost init the biases and the outputs
120 double mean = 0, stddev = 0;
121 this->weights = Eigen::MatrixXd::Zero(numRows, nNeurons);
122
123 // This is going to be used for testing
124 if (this->weightInit == WEIGHT_INIT::CONSTANT) {
125 this->weights = Eigen::MatrixXd::Constant(numRows, nNeurons, 1);
126 return;
127 }
128
129 // calculate mean and stddev based on init algo
130 switch (this->weightInit) {
131 case WEIGHT_INIT::GLOROT:
132 // sqrt(fan_avg)
133 stddev = sqrt(static_cast<double>(2) / (numRows + nNeurons));
134 break;
135 case WEIGHT_INIT::HE:
136 // sqrt(2/fan_in)
137 stddev = sqrt(2.0 / numRows);
138 break;
139 case WEIGHT_INIT::LECUN:
140 // sqrt(1/fan_in)
141 stddev = sqrt(1.0 / numRows);
142 break;
143 default:
144 break;
145 }
146
147 // Init the weights
148 this->weightInit == WEIGHT_INIT::RANDOM
149 ? randomWeightInit(&(this->weights), -1, 1)
150 : randomDistMatrixInit(&(this->weights), mean, stddev);
151 }
152
162 Eigen::MatrixXd computeOutputs(Eigen::MatrixXd inputs,
163 bool training) override {
164 // Initialize the biases based on the input's size
165 if (biases.rows() == 0 && biases.cols() == 0) {
166 biases = Eigen::MatrixXd::Constant(1, nNeurons, bias);
167 }
168
169 // Weighted sum
170 Eigen::MatrixXd wSum = inputs * weights;
171
172 wSum.rowwise() += biases.row(0);
173
174 Eigen::MatrixXd a = activate(wSum);
175
176 // Caching outputs for training
177 if (training) outputs = a;
178
179 return a;
180 };
181
189 void setActivation(ACTIVATION activation) {
190 if (type == LayerType::FLATTEN) {
191 this->activate = Activation::activate;
192 this->diff = Activation::diff;
193 return;
194 }
195
196 switch (activation) {
197 case ACTIVATION::SIGMOID:
198 this->activate = Sigmoid::activate;
199 this->diff = Sigmoid::diff;
200 this->activationSlug = Sigmoid::slug;
201 break;
202 case ACTIVATION::RELU:
203 this->activate = Relu::activate;
204 this->diff = Relu::diff;
205 this->activationSlug = Relu::slug;
206 break;
207 case ACTIVATION::SOFTMAX:
208 this->activate = Softmax::activate;
209 this->diff = Softmax::diff;
210 this->activationSlug = Softmax::slug;
211 break;
215 default:
216 assert(false && "Activation not defined");
217 }
218
219 return;
220 };
221
222 Dense(){}; // Required for serialization
223};
224} // namespace NeuralNet
225
226CEREAL_REGISTER_TYPE(NeuralNet::Dense);
227
228CEREAL_REGISTER_POLYMORPHIC_RELATION(NeuralNet::Layer, NeuralNet::Dense);
static Eigen::MatrixXd diff(const Eigen::MatrixXd &a)
Compute the derivative of the activation function.
Definition Activation.hpp:24
static Eigen::MatrixXd activate(const Eigen::MatrixXd &z)
Activate a layer's outputs.
Definition Activation.hpp:15
Definition Dense.hpp:11
std::string getSlug() const override
Dense layer slug.
Definition Dense.hpp:55
Eigen::MatrixXd getWeights() const
This method gets the layer's weights.
Definition Dense.hpp:28
Eigen::MatrixXd getOutputs() const
This method get the layer's outputs.
Definition Dense.hpp:42
Eigen::MatrixXd getBiases() const
Return the biases of the layer.
Definition Dense.hpp:35
void printWeights()
Method to print layer's weights.
Definition Dense.hpp:47
virtual Eigen::MatrixXd feedInputs(Eigen::MatrixXd inputs, bool training=false) override
This method is used to feed the inputs to the layer.
Definition Dense.hpp:66
Definition Layer.hpp:26
void setOutputs(Eigen::MatrixXd outputs)
Definition Layer.hpp:143