Proyecto Final - Turinmachin
Recreación del minijuego de matemáticas de Brain-Age usando redes neuronales
Loading...
Searching...
No Matches
kan.h
Go to the documentation of this file.
1#ifndef PROG3_NN_FINAL_PROJECT_V2025_01_KAN_H
2#define PROG3_NN_FINAL_PROJECT_V2025_01_KAN_H
3
4#include <cassert>
5#include <cstdint>
6#include <iostream>
7#include <vector>
8#include "interfaces.h"
11
12namespace utec::neural_network {
13 template <typename T>
14 struct BSpline {
15 size_t knots;
16
19
22
23 BSpline(size_t knots, T a, T b)
24 : knots(knots),
25 x_min(a),
26 x_max(b),
27 step((b - a) / (knots - 1)),
28 inv_step(T{1} / step) {}
29
30 auto eval(T x) const -> std::vector<T> {
31 std::vector<T> B(knots, T{0});
32
33 if (x <= x_min) {
34 B[0] = T{1};
35 return B;
36 }
37 if (x >= x_max) {
38 B[knots - 1] = T{1};
39 return B;
40 }
41
42 const T pos = (x - x_min) * inv_step;
43 const auto i = static_cast<size_t>(pos);
44 const T t = pos - i;
45
46 B[i] = T{1} - t;
47 B[i + 1] = t;
48 return B;
49 }
50 };
51
52 template <typename T>
53 class Kan final : public ILayer<T> {
54 size_t in_f;
55 size_t out_f;
56 size_t width;
57 size_t knots;
58
59 T x_min;
60 T x_max;
61
62 algebra::Tensor<T, 3> psi_weights;
63 algebra::Tensor<T, 2> phi_weights;
64 algebra::Tensor<T, 2> phi_biases;
65
66 algebra::Tensor<T, 3> gradient_psi_weights;
67 algebra::Tensor<T, 2> gradient_phi_weights;
68 algebra::Tensor<T, 2> gradient_phi_biases;
69
70 algebra::Tensor<T, 2> input;
71 algebra::Tensor<T, 3> psi_output;
72 algebra::Tensor<T, 2> psi_sum;
73
74 BSpline<T> basis;
75
76 public:
77 Kan(const size_t in_f,
78 const size_t out_f,
79 const size_t knots,
80 const T x_min = -1,
81 const T x_max = +1)
82 : in_f(in_f),
83 out_f(out_f),
84 width((2 * in_f) + 1),
85 knots(knots),
86 x_min(x_min),
87 x_max(x_max),
88 psi_weights(width, in_f, knots),
89 phi_weights(out_f, width),
90 phi_biases(1, out_f),
91 gradient_psi_weights(width, in_f, knots),
92 gradient_phi_weights(out_f, width),
93 gradient_phi_biases(1, out_f),
94 input(0, 0),
95 psi_output(0, 0, 0),
96 psi_sum(0, 0),
97 basis(knots, x_min, x_max) {}
98
99 Kan(const size_t in_f,
100 const size_t out_f,
101 const size_t knots,
102 auto init_psi_w_fun,
103 auto init_phi_w_fun,
104 auto init_phi_b_fun)
105 : Kan(in_f, out_f, knots) {
106 init_psi_w_fun(psi_weights);
107 init_phi_w_fun(phi_weights);
108 init_phi_b_fun(phi_biases);
109 }
110
112 input = x;
113 const size_t B = x.shape()[0];
114
115 psi_output.reshape(B, width, in_f);
116 psi_sum.reshape(B, width);
117
118 for (size_t b = 0; b < B; ++b) {
119 for (size_t q = 0; q < width; ++q) {
120 T sum_q = 0;
121 for (size_t p = 0; p < in_f; ++p) {
122 auto&& Bk = basis.eval(x(b, p));
123 T acc = 0;
124 for (size_t k = 0; k < knots; ++k) {
125 acc += psi_weights(q, p, k) * Bk[k];
126 }
127 psi_output(b, q, p) = acc;
128 sum_q += acc;
129 }
130 psi_sum(b, q) = sum_q;
131 }
132 }
133
134 auto phi_w_T = phi_weights.transpose_2d();
135 auto out = algebra::matrix_product(psi_sum, phi_w_T);
136
137 for (size_t b = 0; b < B; ++b) {
138 for (size_t j = 0; j < out_f; ++j) {
139 out(b, j) += phi_biases(0, j);
140 }
141 }
142
143 return out;
144 }
145
147 const size_t B = input.shape()[0];
148
149 gradient_phi_weights = algebra::matrix_product(dZ.transpose_2d(), psi_sum);
150 // gradient_phi_weights = algebra::transpose_2d(gradient_phi_weights);
151
152 gradient_phi_biases.fill(0);
153 for (size_t j = 0; j < out_f; ++j) {
154 for (size_t b = 0; b < B; ++b) {
155 gradient_phi_biases(0, j) += dZ(b, j);
156 }
157 }
158
159 auto DPsiSum = algebra::matrix_product(dZ, phi_weights);
160
161 gradient_psi_weights.fill(0);
162 algebra::Tensor<T, 2> dInput(B, in_f);
163 dInput.fill(0);
164
165 for (size_t b = 0; b < B; ++b) {
166 for (size_t q = 0; q < width; ++q) {
167 const T dPs = DPsiSum(b, q);
168 for (size_t p = 0; p < in_f; ++p) {
169 auto&& Bk = basis.eval(input(b, p));
170 T weighted_sum = 0;
171 for (size_t k = 0; k < knots; ++k) {
172 const T B_val = Bk[k];
173 gradient_psi_weights(q, p, k) += dPs * B_val;
174 weighted_sum += psi_weights(q, p, k) * B_val;
175 }
176 dInput(b, p) += dPs * weighted_sum;
177 }
178 }
179 }
180
181 return dInput;
182 }
183
184 void update_params(IOptimizer<T>& optimizer) override {
185 for (size_t q = 0; q < width; ++q) {
186 auto psi_slice = psi_weights.slice(q);
187 auto grad_slice = gradient_psi_weights.slice(q);
188 optimizer.update(psi_slice, grad_slice);
189 psi_weights.set_slice(q, psi_slice);
190 }
191 optimizer.update(phi_weights, gradient_phi_weights);
192 optimizer.update(phi_biases, gradient_phi_biases);
193 }
194
195 [[nodiscard]] auto id() const -> LayerId override {
196 return LayerId::Kan;
197 }
198
199 void save(std::ostream& out) const override {
205
206 for (const auto& x : psi_weights) {
208 }
209
210 for (const auto& x : phi_weights) {
212 }
213
214 for (const auto& x : phi_biases) {
216 }
217 }
218
219 static auto load(std::istream& in) -> Kan<T> {
220 auto in_feat = serialization::read_numeric<uint64_t>(in);
221 auto out_feat = serialization::read_numeric<uint64_t>(in);
225
226 Kan<T> layer(in_feat, out_feat, knots, a, b);
227 for (auto& x : layer.psi_weights) {
229 }
230
231 for (auto& x : layer.phi_weights) {
233 }
234
235 for (auto& x : layer.phi_biases) {
237 }
238 return layer;
239 }
240 };
241
242} // namespace utec::neural_network
243
244#endif
return p * x
Definition catch_amalgamated.cpp:321
double p
Definition catch_amalgamated.cpp:251
Kan(const size_t in_f, const size_t out_f, const size_t knots, const T x_min=-1, const T x_max=+1)
Definition kan.h:77
Representa un tensor de tipo T y rango Rank.
Definition tensor.h:63
void fill(const T &value) noexcept
Llena la data de un tesor con un valor.
Definition tensor.h:213
void update_params(IOptimizer< T > &optimizer) override
Actualiza los parámetros internos de la capa (si tiene).
Definition kan.h:184
Kan(const size_t in_f, const size_t out_f, const size_t knots, auto init_psi_w_fun, auto init_phi_w_fun, auto init_phi_b_fun)
Definition kan.h:99
Kan(const size_t in_f, const size_t out_f, const size_t knots, const T x_min=-1, const T x_max=+1)
Definition kan.h:77
static auto load(std::istream &in) -> Kan< T >
Definition kan.h:219
void save(std::ostream &out) const override
Guarda los parámetros internos de la capa en un flujo binario.
Definition kan.h:199
auto backward(const algebra::Tensor< T, 2 > &dZ) -> algebra::Tensor< T, 2 > override
Propagación hacia atrás de la capa.
Definition kan.h:146
auto id() const -> LayerId override
Devuelve el tipo de la capa. Sirve para serialización o reconstrucción de la red.
Definition kan.h:195
auto forward(const algebra::Tensor< T, 2 > &x) -> algebra::Tensor< T, 2 > override
Propagación hacia adelante de la capa.
Definition kan.h:111
auto read_numeric(std::istream &in) -> T
Definition serialization.h:17
void write_numeric(std::ostream &out, const T n)
Definition serialization.h:9
constexpr auto matrix_product(const Tensor< T, 2 > &lhs, const Tensor< T, 2 > &rhs) -> Tensor< T, 2 >
Realiza producto matricial entre 2 tensores de dimension 2.
Definition tensor.h:563
Capa de activación de Rectified Linear Unit (ReLU). Los valores negativos del input se convierten en ...
Definition activation.h:14
LayerId
Identificador para los diferentes tipos de capas en la red neuronal. Se emplea uint8_t (unsigned 8-bi...
Definition interfaces.h:11
@ Kan
Definition interfaces.h:16
T inv_step
Definition kan.h:21
T step
Definition kan.h:20
auto eval(T x) const -> std::vector< T >
Definition kan.h:30
T x_max
Definition kan.h:18
T x_min
Definition kan.h:17
size_t knots
Definition kan.h:15
BSpline(size_t knots, T a, T b)
Definition kan.h:23
Interfaz para definir un optimizador (ej. SGD, Adam, ...). Un optimizador se encarga de actualizar lo...
Definition interfaces.h:26
virtual void update(algebra::Tensor< T, 2 > &params, const algebra::Tensor< T, 2 > &gradients)=0
Actualiza los parámetros del modelo usando los gradientes.