-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRedNeuronal.pde
More file actions
273 lines (235 loc) · 10.6 KB
/
Copy pathRedNeuronal.pde
File metadata and controls
273 lines (235 loc) · 10.6 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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
// ============================================================
// RedNeuronal.pde — Red neuronal feedforward desde cero
// Arquitectura: 6 entradas → 8 neuronas ocultas → 3 salidas
// Activación: tanh en capa oculta, softmax en salida
// Sin librerías externas — todo implementado manualmente
// ============================================================
class RedNeuronal {
// Dimensiones de la red
int cantEntradas = 6;
int cantOcultas = 8;
int cantSalidas = 3;
// Pesos y sesgos entre capas
// pesosEntradaOculta[i][j] = peso de entrada i a neurona oculta j
float[][] pesosEntradaOculta;
float[] sesgosOculta;
// pesosOcultaSalida[i][j] = peso de neurona oculta i a salida j
float[][] pesosOcultaSalida;
float[] sesgosSalida;
// ──────────────────────────────────────────
// Constructor: inicializa pesos aleatorios
// ──────────────────────────────────────────
RedNeuronal() {
pesosEntradaOculta = new float[cantEntradas][cantOcultas];
sesgosOculta = new float[cantOcultas];
pesosOcultaSalida = new float[cantOcultas][cantSalidas];
sesgosSalida = new float[cantSalidas];
inicializarAleatorio();
}
// Constructor copia: clona los pesos de otra red
RedNeuronal(RedNeuronal origen) {
pesosEntradaOculta = new float[cantEntradas][cantOcultas];
sesgosOculta = new float[cantOcultas];
pesosOcultaSalida = new float[cantOcultas][cantSalidas];
sesgosSalida = new float[cantSalidas];
copiarPesos(origen);
}
// ──────────────────────────────────────────
// Inicialización Xavier (mejora convergencia)
// ──────────────────────────────────────────
void inicializarAleatorio() {
float escalaEntOculta = sqrt(2.0 / (cantEntradas + cantOcultas));
float escalaOcuSal = sqrt(2.0 / (cantOcultas + cantSalidas));
for (int i = 0; i < cantEntradas; i++)
for (int j = 0; j < cantOcultas; j++)
pesosEntradaOculta[i][j] = randomGaussiano() * escalaEntOculta;
for (int j = 0; j < cantOcultas; j++)
sesgosOculta[j] = 0;
for (int j = 0; j < cantOcultas; j++)
for (int k = 0; k < cantSalidas; k++)
pesosOcultaSalida[j][k] = randomGaussiano() * escalaOcuSal;
for (int k = 0; k < cantSalidas; k++)
sesgosSalida[k] = 0;
}
// ──────────────────────────────────────────
// Propagación hacia adelante (forward pass)
// entradas: array de 6 valores normalizados [0,1]
// retorna: índice de la acción con mayor activación
// 0 = girar izquierda
// 1 = girar derecha
// 2 = seguir recto
// ──────────────────────────────────────────
int decidirAccion(float[] entradas) {
// Capa oculta con activación tanh
float[] activacionesOcultas = new float[cantOcultas];
for (int j = 0; j < cantOcultas; j++) {
float suma = sesgosOculta[j];
for (int i = 0; i < cantEntradas; i++)
suma += entradas[i] * pesosEntradaOculta[i][j];
activacionesOcultas[j] = tanh(suma);
}
// Capa de salida
float[] activacionesSalida = new float[cantSalidas];
for (int k = 0; k < cantSalidas; k++) {
float suma = sesgosSalida[k];
for (int j = 0; j < cantOcultas; j++)
suma += activacionesOcultas[j] * pesosOcultaSalida[j][k];
activacionesSalida[k] = suma;
}
// Argmax: elegir la acción con mayor valor
int mejorAccion = 0;
for (int k = 1; k < cantSalidas; k++)
if (activacionesSalida[k] > activacionesSalida[mejorAccion])
mejorAccion = k;
return mejorAccion;
}
// Versión que retorna todos los valores de salida (para visualizar la red)
float[] obtenerSalidas(float[] entradas) {
float[] activacionesOcultas = new float[cantOcultas];
for (int j = 0; j < cantOcultas; j++) {
float suma = sesgosOculta[j];
for (int i = 0; i < cantEntradas; i++)
suma += entradas[i] * pesosEntradaOculta[i][j];
activacionesOcultas[j] = tanh(suma);
}
float[] activacionesSalida = new float[cantSalidas];
for (int k = 0; k < cantSalidas; k++) {
float suma = sesgosSalida[k];
for (int j = 0; j < cantOcultas; j++)
suma += activacionesOcultas[j] * pesosOcultaSalida[j][k];
activacionesSalida[k] = suma;
}
return activacionesSalida;
}
// ──────────────────────────────────────────
// Copia profunda de pesos desde otra red
// ──────────────────────────────────────────
void copiarPesos(RedNeuronal origen) {
for (int i = 0; i < cantEntradas; i++)
for (int j = 0; j < cantOcultas; j++)
pesosEntradaOculta[i][j] = origen.pesosEntradaOculta[i][j];
for (int j = 0; j < cantOcultas; j++)
sesgosOculta[j] = origen.sesgosOculta[j];
for (int j = 0; j < cantOcultas; j++)
for (int k = 0; k < cantSalidas; k++)
pesosOcultaSalida[j][k] = origen.pesosOcultaSalida[j][k];
for (int k = 0; k < cantSalidas; k++)
sesgosSalida[k] = origen.sesgosSalida[k];
}
// ──────────────────────────────────────────
// Utilidades matemáticas
// ──────────────────────────────────────────
float tanh(float x) {
// Implementación manual de tanh usando la identidad
float e2x = exp(2 * x);
return (e2x - 1) / (e2x + 1);
}
float randomGaussiano() {
// Box-Muller transform para distribución normal estándar
float u1 = random(0.0001, 1);
float u2 = random(0.0001, 1);
return sqrt(-2 * log(u1)) * cos(TWO_PI * u2);
}
// ──────────────────────────────────────────
// Visualización de la red en pantalla
// Dibuja nodos y conexiones con colores
// según intensidad de los pesos
// ──────────────────────────────────────────
void dibujarRed(float xOrigen, float yOrigen, float anchoPanel, float altoPanel, float[] ultimasEntradas) {
float margenV = 20;
float margenH = 30;
float anchoUtil = anchoPanel - 2 * margenH;
float altoUtil = altoPanel - 2 * margenV;
// Posiciones X de cada capa
float xEntradas = xOrigen + margenH;
float xOcultas = xOrigen + margenH + anchoUtil * 0.45;
float xSalidas = xOrigen + margenH + anchoUtil * 0.90;
float radioNodo = 6;
// Calcular posiciones Y de cada capa
float[] yEntradas = posicionesY(yOrigen + margenV, altoUtil, cantEntradas);
float[] yOcultas = posicionesY(yOrigen + margenV, altoUtil, cantOcultas);
float[] ySalidas = posicionesY(yOrigen + margenV + altoUtil * 0.2, altoUtil * 0.6, cantSalidas);
// Obtener activaciones para colorear nodos
float[] activacionesOcultas = new float[cantOcultas];
float[] activacionesSalida = new float[cantSalidas];
if (ultimasEntradas != null) {
activacionesSalida = obtenerSalidas(ultimasEntradas);
for (int j = 0; j < cantOcultas; j++) {
float suma = sesgosOculta[j];
for (int i = 0; i < cantEntradas; i++)
suma += ultimasEntradas[i] * pesosEntradaOculta[i][j];
activacionesOcultas[j] = tanh(suma);
}
}
strokeWeight(0.5);
// Conexiones entrada → oculta
for (int i = 0; i < cantEntradas; i++) {
for (int j = 0; j < cantOcultas; j++) {
float peso = pesosEntradaOculta[i][j];
colorConexion(peso);
line(xEntradas, yEntradas[i], xOcultas, yOcultas[j]);
}
}
// Conexiones oculta → salida
for (int j = 0; j < cantOcultas; j++) {
for (int k = 0; k < cantSalidas; k++) {
float peso = pesosOcultaSalida[j][k];
colorConexion(peso);
line(xOcultas, yOcultas[j], xSalidas, ySalidas[k]);
}
}
strokeWeight(1);
// Nodos de entrada
String[] etiquetasEntradas = {"Carril", "Dist1", "Carril1", "Tipo1", "Dist2", "Veloc"};
for (int i = 0; i < cantEntradas; i++) {
float activacion = (ultimasEntradas != null) ? ultimasEntradas[i] : 0;
dibujarNodo(xEntradas, yEntradas[i], radioNodo, activacion, etiquetasEntradas[i], true);
}
// Nodos ocultos
for (int j = 0; j < cantOcultas; j++)
dibujarNodo(xOcultas, yOcultas[j], radioNodo, activacionesOcultas[j], "", false);
// Nodos de salida
String[] etiquetasSalidas = {"Izq", "Der", "Recto"};
int mejorSalida = 0;
for (int k = 1; k < cantSalidas; k++)
if (activacionesSalida[k] > activacionesSalida[mejorSalida]) mejorSalida = k;
for (int k = 0; k < cantSalidas; k++) {
color colorSalida = (k == mejorSalida) ? color(50, 220, 100) : color(180, 180, 180);
fill(colorSalida);
stroke(255);
ellipse(xSalidas, ySalidas[k], radioNodo * 2.5, radioNodo * 2.5);
fill(255);
textSize(9);
textAlign(LEFT);
text(etiquetasSalidas[k], xSalidas + radioNodo * 1.5, ySalidas[k] + 3);
}
}
void colorConexion(float peso) {
float intensidad = constrain(abs(peso) / 2.0, 0, 1);
if (peso > 0)
stroke(100, 180, 255, intensidad * 180);
else
stroke(255, 100, 100, intensidad * 180);
}
void dibujarNodo(float x, float y, float radio, float activacion, String etiqueta, boolean mostrarEtiqueta) {
float t = (activacion + 1) / 2.0; // normalizar de [-1,1] a [0,1]
t = constrain(t, 0, 1);
color col = lerpColor(color(40, 40, 80), color(80, 200, 255), t);
fill(col);
stroke(255, 255, 255, 150);
ellipse(x, y, radio * 2, radio * 2);
if (mostrarEtiqueta && etiqueta.length() > 0) {
fill(200, 200, 200);
textSize(8);
textAlign(RIGHT);
text(etiqueta, x - radio - 2, y + 3);
}
}
float[] posicionesY(float yInicio, float altoDisponible, int cantidad) {
float[] posiciones = new float[cantidad];
float separacion = altoDisponible / (cantidad + 1);
for (int i = 0; i < cantidad; i++)
posiciones[i] = yInicio + separacion * (i + 1);
return posiciones;
}
}