-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNetwork.hh
More file actions
154 lines (134 loc) · 4.2 KB
/
Network.hh
File metadata and controls
154 lines (134 loc) · 4.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include <array>
#include <cstddef>
template <typename T, std::size_t width, std::size_t height>
class Array2D
{
public:
explicit constexpr Array2D(float f) : c{f}
{
}
constexpr T& get(std::size_t x, std::size_t y) noexcept
{
return this->c[y * width + x];
}
constexpr T const& get(std::size_t x, std::size_t y) const noexcept
{
return this->c[y * width + x];
}
constexpr auto begin() noexcept
{
return this->c.begin();
}
constexpr auto end() noexcept
{
return this->c.end();
}
private:
std::array<T, width * height> c;
};
template <std::size_t NUM_INPUTS,
std::size_t NUM_NEURONS,
std::size_t... OTHER_LAYERS>
class Network : public Network<NUM_INPUTS, NUM_NEURONS>
{
public:
constexpr static auto learning_rate = 0.5f;
constexpr void compute(std::array<float, NUM_INPUTS> const& inputs) noexcept
{
Network<NUM_INPUTS, NUM_NEURONS>::compute(inputs);
// this->neurons is inherited from Network<NUM_INPUTS, NUM_NEURONS>
this->subnet.compute(this->neurons);
}
constexpr auto const& getSubnet() const noexcept
{
return this->subnet;
}
constexpr auto const& getOutput() const noexcept
{
return this->subnet.getOutput();
}
// `expected` should be a std::array<float, N>, with N being the number of
// neurons on the output layer of the network (last element of OTHER_LAYERS).
constexpr void backprop(auto const& expected) noexcept
{
this->subnet.backprop(expected);
auto const& sublayer_deltas = this->subnet.getDeltas();
auto const sublayer_size = sublayer_deltas.size();
auto const& sublayer_weights = this->subnet.getWeights();
for (auto neuronidx = std::size_t{0}; neuronidx < NUM_NEURONS; ++neuronidx)
{
this->deltas[neuronidx] = 0.f;
for (auto subneuronidx = std::size_t{0}; subneuronidx < sublayer_size;
++subneuronidx)
this->deltas[neuronidx] +=
sublayer_deltas[subneuronidx] *
sublayer_weights.get(subneuronidx, neuronidx);
for (auto inputidx = std::size_t{0}; inputidx < NUM_INPUTS; ++inputidx)
this->weights.get(neuronidx, inputidx) +=
learning_rate * this->deltas[neuronidx] *
(this->neurons[neuronidx] > 0 ? 1 : 0);
}
}
private:
Network<NUM_NEURONS, OTHER_LAYERS...> subnet;
};
template <std::size_t NUM_INPUTS, std::size_t NUM_NEURONS>
class Network<NUM_INPUTS, NUM_NEURONS>
{
public:
constexpr static auto learning_rate = 0.5f;
// For testing purpose only
constexpr Network() noexcept : neurons{1.f}, deltas{1.f}, weights{1.f}
{
for (auto& neuron : this->neurons)
neuron = 0.5f;
for (auto& weight : this->weights)
weight = 0.5f;
}
constexpr auto const& getNeurons() const noexcept
{
return this->neurons;
}
constexpr auto const& getDeltas() const noexcept
{
return this->deltas;
}
constexpr auto const& getWeights() const noexcept
{
return this->weights;
}
constexpr void compute(std::array<float, NUM_INPUTS> const& inputs) noexcept
{
for (auto neuronidx = std::size_t{0}; neuronidx < NUM_NEURONS; ++neuronidx)
{
auto net_input = float{0.f};
for (auto inputidx = std::size_t{0}; inputidx < NUM_INPUTS; ++inputidx)
net_input += inputs[inputidx] * this->weights.get(neuronidx, inputidx);
this->neurons[neuronidx] = activation_function(net_input);
}
}
constexpr void backprop(
std::array<float, NUM_NEURONS> const& expected) noexcept
{
for (auto neuronidx = std::size_t{0}; neuronidx < NUM_NEURONS; ++neuronidx)
{
this->deltas[neuronidx] = expected[neuronidx] - this->neurons[neuronidx];
for (auto inputidx = std::size_t{0}; inputidx < NUM_INPUTS; ++inputidx)
this->weights.get(neuronidx, inputidx) +=
learning_rate * this->deltas[neuronidx] * expected[neuronidx] *
(this->neurons[neuronidx] > 0 ? 1 : 0);
}
}
constexpr auto const& getOutput() const noexcept
{
return this->neurons;
}
protected:
constexpr static float activation_function(float net_input) noexcept
{
return net_input > 0 ? net_input : 0;
}
std::array<float, NUM_NEURONS> neurons;
std::array<float, NUM_NEURONS> deltas;
Array2D<float, NUM_NEURONS, NUM_INPUTS> weights;
};