grx-tensor 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/GUIA_PRINCIPIANTES.md +1046 -0
- data/README.es.md +1199 -0
- data/README.md +1026 -283
- data/ext/grx/extconf.rb +4 -18
- data/ext/grx/grx_core.c +411 -331
- data/ext/grx/grx_core.h +23 -13
- data/ext/unix/Makefile +3 -27
- data/ext/windows/Makefile.mingw +3 -23
- data/grx-tensor.gemspec +41 -35
- data/lib/grx/c_api.rb +75 -39
- data/lib/grx/data.rb +87 -0
- data/lib/grx/loss.rb +55 -25
- data/lib/grx/nn.rb +160 -41
- data/lib/grx/optim.rb +21 -16
- data/lib/grx/serialization.rb +66 -0
- data/lib/grx/storage.rb +19 -27
- data/lib/grx/tensor.rb +234 -65
- data/lib/grx/utils.rb +43 -0
- data/lib/grx/version.rb +1 -1
- data/lib/grx.rb +22 -6
- metadata +37 -28
data/README.es.md
ADDED
|
@@ -0,0 +1,1199 @@
|
|
|
1
|
+
# GRX-Tensor
|
|
2
|
+
|
|
3
|
+
**Ruby habla. C calcula.**
|
|
4
|
+
|
|
5
|
+
Un framework de computacion cientifica, procesamiento tensorial multidimensional y Deep Learning de alto rendimiento para Ruby. Cuenta con diferenciacion automatica (Autograd), aceleracion de hardware multi-target SIMD dinamica (AVX2+FMA, SSE y escalar C portable), y primitivas completas para redes neuronales, todo respaldado por una API de Ruby limpia, intuitiva y expresiva.
|
|
6
|
+
|
|
7
|
+
[](https://www.ruby-lang.org)
|
|
8
|
+
[](LICENSE.txt)
|
|
9
|
+
[](https://github.com/Gabo-Razo/grx-tensor)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Flujo Arquitectonico del Sistema
|
|
14
|
+
|
|
15
|
+
```mermaid
|
|
16
|
+
flowchart TD
|
|
17
|
+
subgraph AppUsuario["Aplicacion del Usuario"]
|
|
18
|
+
A["require 'grx'"] --> B["GRX.tensor(datos, shape)"]
|
|
19
|
+
B --> C["Computacion Cientifica / Modelos de Machine Learning"]
|
|
20
|
+
C --> D["Bucles de Entrenamiento, Simulaciones e Inferencia"]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
subgraph CapaRuby["Motor de Alto Nivel en Ruby"]
|
|
24
|
+
E["GRX::Tensor (Shape Multidimensional, Strides, Nodos del Grafo DAG)"]
|
|
25
|
+
F["GRX::NN (Linear, Embedding, LayerNorm, Dropout, BatchNorm1d)"]
|
|
26
|
+
G["GRX::Loss (CrossEntropy, MSE, BCE, Huber, MAE)"]
|
|
27
|
+
H["GRX::Optim (Adam con FMA, SGD con Momento)"]
|
|
28
|
+
I["GRX::Data (TensorDataset, DataLoader)"]
|
|
29
|
+
J["GRX::Serialization (Motor Binario .grx de Alta Velocidad)"]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
subgraph SubsistemaFFI["Puente FFI y Memoria (Fiddle)"]
|
|
33
|
+
K["GRX::Storage (Punteros de Memoria Alineada / Finalizador GC)"]
|
|
34
|
+
L["GRX::CAPI (Despacho Dinamico de Simbolos Multi-Biblioteca)"]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
subgraph NucleoNativo["Nucleo Nativo C (ext/grx/grx_core.c)"]
|
|
38
|
+
M{"grx_simd_level() Deteccion de Hardware"}
|
|
39
|
+
N["Motor AVX2 + FMA (4 doubles/ciclo, Multiply-Add Fusionado)"]
|
|
40
|
+
O["Motor SSE (2 doubles/ciclo, Math Vectorizado)"]
|
|
41
|
+
P["C Escalar Universal (Matematica IEEE 754 Portable)"]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
subgraph Hardware["Capa de Hardware"]
|
|
45
|
+
Q["Memoria Heap Alineada a 32 Bytes"]
|
|
46
|
+
R["Cache Tiling L1/L2 (Bloques de Lineas de Cache de 64 Bytes)"]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
AppUsuario --> CapaRuby
|
|
50
|
+
CapaRuby --> SubsistemaFFI
|
|
51
|
+
SubsistemaFFI --> NucleoNativo
|
|
52
|
+
M -- "CPU con AVX2+FMA" --> N
|
|
53
|
+
M -- "CPU con SSE2/SSE4" --> O
|
|
54
|
+
M -- "Generico / ARM / VM" --> P
|
|
55
|
+
N --> Hardware
|
|
56
|
+
O --> Hardware
|
|
57
|
+
P --> Hardware
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Compilacion Universal y Despacho Dinamico de Hardware
|
|
63
|
+
|
|
64
|
+
```mermaid
|
|
65
|
+
flowchart TD
|
|
66
|
+
A["gem install grx-tensor"] --> B["Gestor de Paquetes RubyGems"]
|
|
67
|
+
B --> C["ext/grx/extconf.rb (mkmf)"]
|
|
68
|
+
|
|
69
|
+
C --> D1["Linux (GCC / Clang)"]
|
|
70
|
+
C --> D2["macOS (Clang / Apple LLVM)"]
|
|
71
|
+
C --> D3["Windows (RubyInstaller DevKit / MinGW-w64)"]
|
|
72
|
+
|
|
73
|
+
D1 --> E1["libgrx_core.so"]
|
|
74
|
+
D2 --> E2["libgrx_core.dylib"]
|
|
75
|
+
D3 --> E3["grx_core.dll"]
|
|
76
|
+
|
|
77
|
+
E1 --> F["Binario Multi-Target Universal"]
|
|
78
|
+
E2 --> F
|
|
79
|
+
E3 --> F
|
|
80
|
+
|
|
81
|
+
F --> G{"Deteccion de Instrucciones de CPU en Ejecucion"}
|
|
82
|
+
G -- "AVX2 + FMA Detectado" --> H["Ejecuta SIMD AVX2+FMA (Maxima Velocidad)"]
|
|
83
|
+
G -- "SSE2 Detectado" --> I["Ejecuta Kernels Vectorizados SSE"]
|
|
84
|
+
G -- "ARM / VM / CPU Antigua" --> J["Ejecuta C Escalar Seguro (Cero Fallos de Hardware)"]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Tabla de Contenido
|
|
90
|
+
|
|
91
|
+
1. [Caracteristicas Principales](#caracteristicas-principales)
|
|
92
|
+
2. [Instalacion](#instalacion)
|
|
93
|
+
3. [Tutoriales Basicos de Inicio Rapido](#tutoriales-basicos-de-inicio-rapido)
|
|
94
|
+
- [1. Conversor de Grados Celsius a Fahrenheit](#1-conversor-de-grados-celsius-a-fahrenheit-1-neurona-aprende-f--18c--32)
|
|
95
|
+
- [2. Compuertas Logicas (AND y XOR)](#2-compuertas-logicas-and-lineal-vs-xor-no-lineal-con-relu-y-sigmoide)
|
|
96
|
+
- [3. Matematicas Tensoriales Cotidianas](#3-matematicas-tensoriales-cotidianas-en-4-lineas)
|
|
97
|
+
4. [Manual de Referencia del API de Tensores](#manual-de-referencia-del-api-de-tensores)
|
|
98
|
+
5. [Autograd y Motor de Diferenciacion](#autograd-y-motor-de-diferenciacion)
|
|
99
|
+
6. [Computacion Cientifica y Numerica (Mas Alla del Deep Learning)](#computacion-cientifica-y-numerica-mas-alla-del-deep-learning)
|
|
100
|
+
- [A. Vision por Computadora y Filtrado de Imagenes (Convolucion Sobel)](#a-vision-por-computadora-y-filtrado-de-imagenes-convolucion-sobel)
|
|
101
|
+
- [B. Finanzas Cuantitativas y Matriz de Covarianza](#b-finanzas-cuantitativas-y-matriz-de-covarianza)
|
|
102
|
+
- [C. Fisica de Particulas y Simulacion N-Cuerpos](#c-fisica-de-particulas-y-simulacion-n-cuerpos)
|
|
103
|
+
- [D. Optimizacion Matematica Pura con Autograd (Funcion Rosenbrock)](#d-optimizacion-matematica-pura-con-autograd-funcion-rosenbrock)
|
|
104
|
+
7. [Recetario de Deep Learning (10 Arquitecturas de Redes Neuronales)](#recetario-de-deep-learning-10-arquitecturas-de-redes-neuronales)
|
|
105
|
+
- [Arquitectura 1: Clasificador de Vision y Caracteres (BatchNorm + Dropout)](#arquitectura-1-clasificador-de-vision-y-caracteres-batchnorm--dropout)
|
|
106
|
+
- [Arquitectura 2: NLP y Chatbot de Intenciones (Embedding + LayerNorm)](#arquitectura-2-nlp-y-chatbot-de-intenciones-embedding--layernorm)
|
|
107
|
+
- [Arquitectura 3: Aprendizaje por Refuerzo (Agente Deep Q-Network DQN)](#arquitectura-3-aprendizaje-por-refuerzo-agente-deep-q-network-dqn)
|
|
108
|
+
- [Arquitectura 4: Pronostico No Lineal de Series Temporales Multivariables](#arquitectura-4-pronostico-no-lineal-de-series-temporales-multivariables)
|
|
109
|
+
- [Arquitectura 5: Autoencoder Profundo para Reduccion Dimensional y Deteccion de Anomalias](#arquitectura-5-autoencoder-profundo-para-reduccion-dimensional-y-deteccion-de-anomalias)
|
|
110
|
+
- [Arquitectura 6: Modelo Generador de Lenguaje y Siguiente Caracter Autoregresivo](#arquitectura-6-modelo-generador-de-lenguaje-y-siguiente-caracter-autoregresivo)
|
|
111
|
+
- [Arquitectura 7: Analisis de Sentimiento y Clasificacion de Resenas (BCELoss)](#arquitectura-7-analisis-de-sentimiento-y-clasificacion-de-resenas-bceloss)
|
|
112
|
+
- [Arquitectura 8: Red Neuronal Siamesa para Verificacion de Similitud y Firmas](#arquitectura-8-red-neuronal-siamesa-para-verificacion-de-similitud-y-firmas)
|
|
113
|
+
- [Arquitectura 9: Red Residual Profunda (Bloque ResNet MLP con Conexion Skip)](#arquitectura-9-red-residual-profunda-bloque-resnet-mlp-con-conexion-skip)
|
|
114
|
+
- [Arquitectura 10: Filtrado Colaborativo Neuronal y Sistema de Recomendacion](#arquitectura-10-filtrado-colaborativo-neuronal-y-sistema-de-recomendacion)
|
|
115
|
+
8. [Funciones de Perdida (GRX::Loss)](#funciones-de-perdida-grxloss)
|
|
116
|
+
9. [Guia de Notacion Cientifica y Parametros](#guia-de-notacion-cientifica-y-parametros)
|
|
117
|
+
- [1. Que significa la Notacion Cientifica en Machine Learning (1e-1 a 1e-8)?](#1-que-significa-la-notacion-cientifica-en-machine-learning-1e-1-a-1e-8)
|
|
118
|
+
- [2. Parametros de Capas Neuronales (GRX::NN)](#2-parametros-de-capas-neuronales-grxnn)
|
|
119
|
+
10. [Catalogo de Optimizadores e Hiperparametros (GRX::Optim)](#catalogo-de-optimizadores-e-hiperparametros-grxoptim)
|
|
120
|
+
11. [Pipelines de Datos y DataLoader (GRX::Data)](#pipelines-de-datos-y-dataloader-grxdata)
|
|
121
|
+
12. [Persistencia de Modelos y Cerebros (Formato .grx)](#persistencia-de-modelos-y-cerebros-formato-grx)
|
|
122
|
+
- [Especificacion del Formato Binario GRX1](#especificacion-del-formato-binario-grx1)
|
|
123
|
+
- [Flujo de Despliegue de Inferencia en Produccion](#flujo-de-despliegue-de-inferencia-en-produccion)
|
|
124
|
+
13. [Gestion de Gradientes y Utilidades (GRX::Utils)](#gestion-de-gradientes-y-utilidades-grxutils)
|
|
125
|
+
14. [Guia de Soporte y Herramientas para Windows](#guia-de-soporte-y-herramientas-para-windows)
|
|
126
|
+
15. [Licencia](#licencia)
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Caracteristicas Principales
|
|
131
|
+
|
|
132
|
+
| Caracteristica | Especificacion |
|
|
133
|
+
|---|---|
|
|
134
|
+
| **Motor SIMD Multi-Target** | Deteccion dinamica en ejecucion: AVX2+FMA, SSE o escalar C portable |
|
|
135
|
+
| **Memoria Heap Alineada** | Asignacion en heap alineada a 32 bytes (`posix_memalign` / `_aligned_malloc`) |
|
|
136
|
+
| **Vistas Zero-Copy** | Transformaciones geometricas por zancadas (`reshape`, `transpose`, `flatten`, `t`) |
|
|
137
|
+
| **Motor Autograd** | Diferenciacion automatica en modo inverso con retropropagacion dinamica de DAG |
|
|
138
|
+
| **Capas Neuronales** | `Linear`, `Sequential`, `Embedding`, `LayerNorm`, `BatchNorm1d`, `Dropout` |
|
|
139
|
+
| **Funciones de Activacion** | `ReLU`, `LeakyReLU`, `Sigmoid`, `Tanh`, `Softmax` (totalmente diferenciables) |
|
|
140
|
+
| **Funciones de Perdida** | `MSELoss`, `MAELoss`, `BCELoss`, `CrossEntropyLoss`, `HuberLoss` |
|
|
141
|
+
| **Optimizadores** | `Adam` (vectorizado en C con FMA y correccion de sesgo), `SGD` (con momento y decay) |
|
|
142
|
+
| **Persistencia Binaria** | Formato `.grx` ultrarrapido para guardar y cargar pesos al instante |
|
|
143
|
+
| **Pipelines de Datos** | `TensorDataset` y `DataLoader` con division en lotes (*batching*) y barajado (*shuffle*) |
|
|
144
|
+
| **Inicializadores de Pesos** | Xavier uniforme y He normal (xorshift64* y Box-Muller en C) |
|
|
145
|
+
| **Multiplataforma** | Linux (`.so`), macOS (`.dylib`), Windows (`.dll` mediante DevKit) |
|
|
146
|
+
| **Fallback en Ruby Puro** | Ejecuta fluidamente en Ruby puro si el compilador de C no esta presente |
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Instalacion
|
|
151
|
+
|
|
152
|
+
### Instalacion Estandar con RubyGems
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
gem install grx-tensor
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
O agregalo al `Gemfile` de tu proyecto:
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
gem "grx-tensor"
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
La extension nativa en C se compila y enlaza de forma automatica y transparente durante la instalacion.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## Tutoriales Basicos de Inicio Rapido
|
|
169
|
+
|
|
170
|
+
### 1. Conversor de Grados Celsius a Fahrenheit (1 Neurona Aprende $F = 1.8C + 32$)
|
|
171
|
+
|
|
172
|
+
El "Hello World" por excelencia del Machine Learning. Una sola neurona lineal ($y = w \cdot x + b$) aprende la relacion termica por si sola:
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
require "grx"
|
|
176
|
+
|
|
177
|
+
# 1. Datos de entrenamiento (Pares reales de Celsius y Fahrenheit)
|
|
178
|
+
celsius = GRX.tensor([18.0, 25.0, 14.0, 21.0, 9.0, 16.0, 4.0, 32.0], [8, 1])
|
|
179
|
+
fahrenheit = GRX.tensor([64.4, 77.0, 57.2, 69.8, 48.2, 60.8, 39.2, 89.6], [8, 1])
|
|
180
|
+
|
|
181
|
+
# 2. Modelo de 1 neurona
|
|
182
|
+
modelo = GRX::NN::Sequential.new(GRX::NN::Linear.new(1, 1))
|
|
183
|
+
optimizador = GRX::Optim::Adam.new(modelo.parameters, lr: 0.8)
|
|
184
|
+
funcion_error = GRX::Loss::MSELoss.new
|
|
185
|
+
|
|
186
|
+
# 3. Entrenamiento en 5 lineas
|
|
187
|
+
1500.times do
|
|
188
|
+
optimizador.zero_grad
|
|
189
|
+
prediccion = modelo.call(celsius)
|
|
190
|
+
error = funcion_error.call(prediccion, fahrenheit)
|
|
191
|
+
error.backward
|
|
192
|
+
optimizador.step
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# 4. Prediccion de temperaturas nunca antes vistas
|
|
196
|
+
temperaturas_test = GRX.tensor([[100.0], [0.0], [37.0]], [3, 1])
|
|
197
|
+
predicciones = modelo.call(temperaturas_test).to_a
|
|
198
|
+
|
|
199
|
+
puts "100.0 C -> #{predicciones[0].round(1)} F (Esperado: 212.0 F)"
|
|
200
|
+
puts " 0.0 C -> #{predicciones[1].round(1)} F (Esperado: 32.0 F)"
|
|
201
|
+
puts " 37.0 C -> #{predicciones[2].round(1)} F (Esperado: 98.6 F)"
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
### 2. Compuertas Logicas (AND Lineal vs XOR No Lineal con ReLU y Sigmoide)
|
|
207
|
+
|
|
208
|
+
Resolucion del clasico problema no lineal XOR mediante una red neuronal de 2 capas:
|
|
209
|
+
|
|
210
|
+
```ruby
|
|
211
|
+
require "grx"
|
|
212
|
+
|
|
213
|
+
# Tabla de verdad XOR: (0,0)->0, (0,1)->1, (1,0)->1, (1,1)->0
|
|
214
|
+
x = GRX.tensor([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]], [4, 2])
|
|
215
|
+
y = GRX.tensor([[0.0], [1.0], [1.0], [0.0]], [4, 1])
|
|
216
|
+
|
|
217
|
+
# Red neuronal: 2 entradas -> 4 ocultas (ReLU) -> 1 salida (Sigmoide)
|
|
218
|
+
red_xor = GRX::NN::Sequential.new(
|
|
219
|
+
GRX::NN::Linear.new(2, 4),
|
|
220
|
+
GRX::NN::ReLU.new,
|
|
221
|
+
GRX::NN::Linear.new(4, 1),
|
|
222
|
+
GRX::NN::Sigmoid.new
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
opt = GRX::Optim::Adam.new(red_xor.parameters, lr: 0.05)
|
|
226
|
+
loss_fn = GRX::Loss::BCELoss.new
|
|
227
|
+
|
|
228
|
+
400.times do
|
|
229
|
+
opt.zero_grad
|
|
230
|
+
pred = red_xor.call(x)
|
|
231
|
+
loss = loss_fn.call(pred, y)
|
|
232
|
+
loss.backward
|
|
233
|
+
opt.step
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
puts "Predicciones XOR: #{red_xor.call(x).to_a.map { |v| v.round(3) }}"
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### 3. Matematicas Tensoriales Cotidianas en 4 Lineas
|
|
242
|
+
|
|
243
|
+
```ruby
|
|
244
|
+
require "grx"
|
|
245
|
+
|
|
246
|
+
precios = GRX.tensor([19.99, 45.50, 120.00, 5.25], [4])
|
|
247
|
+
con_descuento = precios * 0.85 # 15% de descuento directo en C con SIMD
|
|
248
|
+
|
|
249
|
+
puts "Ingreso total: #{precios.sum.item.round(2)}"
|
|
250
|
+
puts "Precio promedio: #{precios.mean.item.round(2)}"
|
|
251
|
+
puts "Indice del articulo mas costoso: #{precios.argmax}"
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Manual de Referencia del API de Tensores
|
|
257
|
+
|
|
258
|
+
Los tensores en GRX representan arreglos multidimensionales de numeros de punto flotante de doble precision (IEEE 754 de 64 bits) alojados en buffers contiguos de memoria nativa.
|
|
259
|
+
|
|
260
|
+
### 1. Creacion y Metodos de Fabrica
|
|
261
|
+
|
|
262
|
+
```ruby
|
|
263
|
+
require "grx"
|
|
264
|
+
|
|
265
|
+
# 1. Desde arreglos planos o anidados de Ruby (enteros o flotantes)
|
|
266
|
+
t1 = GRX.tensor([1.0, 2.0, 3.0, 4.0], [2, 2])
|
|
267
|
+
t2 = GRX.tensor([[1.0, 2.0], [3.0, 4.0]], [2, 2], requires_grad: true)
|
|
268
|
+
|
|
269
|
+
# 2. Tensores de Ceros y Unos
|
|
270
|
+
ceros = GRX.zeros([3, 4])
|
|
271
|
+
unos = GRX.ones([2, 5], requires_grad: true)
|
|
272
|
+
|
|
273
|
+
# 3. Inicializacion Aleatoria
|
|
274
|
+
aleatorio_u = GRX.rand([4, 4]) # Distribucion uniforme U[0, 1)
|
|
275
|
+
aleatorio_n = GRX.randn([4, 4]) # Distribucion normal estandar N(0, 1)
|
|
276
|
+
|
|
277
|
+
# 4. Inicializacion de Pesos para Redes Neuronales
|
|
278
|
+
xavier = GRX::Tensor.xavier_uniform([64, 32], requires_grad: true)
|
|
279
|
+
he = GRX::Tensor.he_normal([64, 32], requires_grad: true)
|
|
280
|
+
|
|
281
|
+
# 5. Fabricas basadas en otro tensor
|
|
282
|
+
z_like = GRX::Tensor.zeros_like(t1)
|
|
283
|
+
o_like = GRX::Tensor.ones_like(t1)
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### 2. Inspeccion y Acceso a Elementos
|
|
287
|
+
|
|
288
|
+
```ruby
|
|
289
|
+
t = GRX.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3])
|
|
290
|
+
|
|
291
|
+
puts t.shape # [2, 3] (Dimensiones)
|
|
292
|
+
puts t.strides # [3, 1] (Saltos en memoria)
|
|
293
|
+
puts t.numel # 6 (Numero total de elementos)
|
|
294
|
+
puts t.rank # 2 (Rango / Numero de dimensiones)
|
|
295
|
+
puts t.item # Retorna el float escalar si numel == 1
|
|
296
|
+
puts t.to_a # [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
|
|
297
|
+
puts t.get(1, 2) # 6.0 (Elemento en fila 1, columna 2)
|
|
298
|
+
t.set(1, 2, 9.9) # Modifica el elemento en (1, 2)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### 3. Operaciones Aritmeticas (Aceleradas por SIMD)
|
|
302
|
+
|
|
303
|
+
```ruby
|
|
304
|
+
a = GRX.tensor([1.0, 2.0, 3.0], [3])
|
|
305
|
+
b = GRX.tensor([4.0, 5.0, 6.0], [3])
|
|
306
|
+
|
|
307
|
+
c_suma = a + b # [5.0, 7.0, 9.0]
|
|
308
|
+
c_resta = a - b # [-3.0, -3.0, -3.0]
|
|
309
|
+
c_mult = a * b # [4.0, 10.0, 18.0]
|
|
310
|
+
c_div = b / a # [4.0, 2.5, 2.0]
|
|
311
|
+
c_neg = -a # [-1.0, -2.0, -3.0]
|
|
312
|
+
|
|
313
|
+
# Aritmetica con escalares
|
|
314
|
+
s_suma = a + 10.0 # [11.0, 12.0, 13.0]
|
|
315
|
+
s_mult = a * 2.0 # [2.0, 4.0, 6.0]
|
|
316
|
+
s_div = a / 2.0 # [0.5, 1.0, 1.5]
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### 4. Funciones Matematicas Element-Wise
|
|
320
|
+
|
|
321
|
+
```ruby
|
|
322
|
+
t = GRX.tensor([1.0, 4.0, 9.0, 16.0], [4])
|
|
323
|
+
|
|
324
|
+
puts t.sqrt.to_a # [1.0, 2.0, 3.0, 4.0] (Raiz cuadrada)
|
|
325
|
+
puts t.square.to_a # [1.0, 16.0, 81.0, 256.0] (Cuadrado)
|
|
326
|
+
puts t.abs.to_a # [1.0, 4.0, 9.0, 16.0] (Valor absoluto)
|
|
327
|
+
puts t.pow(3.0).to_a # [1.0, 64.0, 729.0, 4096.0] (Potencia)
|
|
328
|
+
puts t.log.to_a # Logaritmo natural ln(x)
|
|
329
|
+
puts t.exp.to_a # Exponencial e^x
|
|
330
|
+
puts t.clip(2.0, 10.0) # Limita los valores al rango [2.0, 10.0]
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### 5. Multiplicacion de Matrices y Algebra Lineal
|
|
334
|
+
|
|
335
|
+
```ruby
|
|
336
|
+
# Multiplicacion matricial (SIMD con Cache Tiling en L1)
|
|
337
|
+
m1 = GRX.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3])
|
|
338
|
+
m2 = GRX.tensor([7.0, 8.0, 9.0, 10.0, 11.0, 12.0], [3, 2])
|
|
339
|
+
|
|
340
|
+
resultado = m1 @ m2 # Equivalente a m1.matmul(m2), retorna Shape [2, 2]
|
|
341
|
+
|
|
342
|
+
# Producto punto (vectores 1D)
|
|
343
|
+
v1 = GRX.tensor([1.0, 2.0, 3.0], [3])
|
|
344
|
+
v2 = GRX.tensor([4.0, 5.0, 6.0], [3])
|
|
345
|
+
punto = v1.dot(v2) # 32.0 (Escalar double)
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### 6. Transformaciones Geometricas (Vistas Zero-Copy)
|
|
349
|
+
|
|
350
|
+
```ruby
|
|
351
|
+
matriz = GRX.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3])
|
|
352
|
+
|
|
353
|
+
# Reshape y Aplanado
|
|
354
|
+
reestructurado = matriz.reshape([3, 2]) # Shape [3, 2]
|
|
355
|
+
plano = matriz.flatten # Shape [6]
|
|
356
|
+
|
|
357
|
+
# Transposicion
|
|
358
|
+
transpuesta = matriz.transpose(0, 1) # Shape [3, 2]
|
|
359
|
+
t_rapida = matriz.t # Alias para transposicion 2D
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
### 7. Reducciones y Estadisticas
|
|
363
|
+
|
|
364
|
+
```ruby
|
|
365
|
+
t = GRX.tensor([2.0, 4.0, 6.0, 8.0], [4], requires_grad: true)
|
|
366
|
+
|
|
367
|
+
s = t.sum # Tensor escalar [20.0], nodo autograd
|
|
368
|
+
m = t.mean # Tensor escalar [5.0], nodo autograd
|
|
369
|
+
max_v = t.max # 8.0 (Float escalar)
|
|
370
|
+
min_v = t.min # 2.0 (Float escalar)
|
|
371
|
+
max_idx = t.argmax # 3 (Indice del valor maximo)
|
|
372
|
+
min_idx = t.argmin # 0 (Indice del valor minimo)
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Autograd y Motor de Diferenciacion
|
|
378
|
+
|
|
379
|
+
GRX incorpora un motor dinamico de diferenciacion automatica en modo inverso basado en un Grafo Aciclico Dirigido (DAG). Al invocar `backward` sobre un tensor escalar, las derivadas parciales se propagan hacia atras en todas las ramas computacionales.
|
|
380
|
+
|
|
381
|
+
```mermaid
|
|
382
|
+
flowchart LR
|
|
383
|
+
A["Tensor a (requires_grad: true)"] --> C["Multiplicacion (a * b)"]
|
|
384
|
+
B["Tensor b (requires_grad: true)"] --> C
|
|
385
|
+
C --> D["Suma (+ 2.0)"]
|
|
386
|
+
D --> E["Reduccion (.sum)"]
|
|
387
|
+
E --> F["Perdida Escalar"]
|
|
388
|
+
F -. "loss.backward" .-> E
|
|
389
|
+
E -. "dLoss/dD" .-> D
|
|
390
|
+
D -. "dLoss/dC" .-> C
|
|
391
|
+
C -. "a.grad = dLoss/da" .-> A
|
|
392
|
+
C -. "b.grad = dLoss/db" .-> B
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
```ruby
|
|
396
|
+
require "grx"
|
|
397
|
+
|
|
398
|
+
x = GRX.tensor([2.0, 3.0], [2], requires_grad: true)
|
|
399
|
+
w = GRX.tensor([4.0, 5.0], [2], requires_grad: true)
|
|
400
|
+
b = GRX.tensor([1.0, 1.0], [2], requires_grad: true)
|
|
401
|
+
|
|
402
|
+
y = (x * w) + b
|
|
403
|
+
loss = y.sum
|
|
404
|
+
loss.backward
|
|
405
|
+
|
|
406
|
+
puts "x.grad: #{x.grad.to_a}" # [4.0, 5.0]
|
|
407
|
+
puts "w.grad: #{w.grad.to_a}" # [2.0, 3.0]
|
|
408
|
+
puts "b.grad: #{b.grad.to_a}" # [1.0, 1.0]
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## Computacion Cientifica y Numerica (Mas Alla del Deep Learning)
|
|
414
|
+
|
|
415
|
+
Los tensores son motores matematicos multidimensionales de proposito general ideales para procesamiento de senales, finanzas cuantitativas, fisica computacional y optimizacion matematica.
|
|
416
|
+
|
|
417
|
+
### A. Vision por Computadora y Filtrado de Imagenes (Convolucion Sobel)
|
|
418
|
+
|
|
419
|
+
Aplica filtrado espacial y deteccion de bordes directamente sobre matrices de pixeles:
|
|
420
|
+
|
|
421
|
+
```ruby
|
|
422
|
+
require "grx"
|
|
423
|
+
|
|
424
|
+
imagen = GRX.tensor([
|
|
425
|
+
0.0, 0.0, 0.0, 255.0, 255.0, 255.0,
|
|
426
|
+
0.0, 0.0, 0.0, 255.0, 255.0, 255.0,
|
|
427
|
+
0.0, 0.0, 0.0, 255.0, 255.0, 255.0,
|
|
428
|
+
0.0, 0.0, 0.0, 255.0, 255.0, 255.0,
|
|
429
|
+
0.0, 0.0, 0.0, 255.0, 255.0, 255.0,
|
|
430
|
+
0.0, 0.0, 0.0, 255.0, 255.0, 255.0
|
|
431
|
+
], [6, 6])
|
|
432
|
+
|
|
433
|
+
sobel_h = GRX.tensor([
|
|
434
|
+
-1.0, 0.0, 1.0,
|
|
435
|
+
-2.0, 0.0, 2.0,
|
|
436
|
+
-1.0, 0.0, 1.0
|
|
437
|
+
], [3, 3])
|
|
438
|
+
|
|
439
|
+
filas_out = imagen.shape[0] - sobel_h.shape[0] + 1
|
|
440
|
+
cols_out = imagen.shape[1] - sobel_h.shape[1] + 1
|
|
441
|
+
mapa_bordes = []
|
|
442
|
+
|
|
443
|
+
filas_out.times do |r|
|
|
444
|
+
cols_out.times do |c|
|
|
445
|
+
parche = []
|
|
446
|
+
3.times { |kr| 3.times { |kc| parche << imagen.get(r + kr, c + kc) } }
|
|
447
|
+
tensor_parche = GRX.tensor(parche, [3, 3])
|
|
448
|
+
conv_val = (tensor_parche * sobel_h).sum.item
|
|
449
|
+
mapa_bordes << conv_val.abs
|
|
450
|
+
end
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
bordes = GRX.tensor(mapa_bordes, [filas_out, cols_out])
|
|
454
|
+
puts "Dimension del Mapa de Bordes: #{bordes.shape}"
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
### B. Finanzas Cuantitativas y Matriz de Covarianza
|
|
460
|
+
|
|
461
|
+
Calcula retornos diarios, volatilidad anualizada y riesgo de portafolios de inversion:
|
|
462
|
+
|
|
463
|
+
```ruby
|
|
464
|
+
require "grx"
|
|
465
|
+
|
|
466
|
+
precios = GRX.tensor([
|
|
467
|
+
100.0, 50.0, 200.0,
|
|
468
|
+
102.0, 49.0, 205.0,
|
|
469
|
+
101.0, 51.0, 210.0,
|
|
470
|
+
105.0, 52.0, 208.0,
|
|
471
|
+
108.0, 53.0, 215.0
|
|
472
|
+
], [5, 3])
|
|
473
|
+
|
|
474
|
+
retornos_data = []
|
|
475
|
+
4.times do |t|
|
|
476
|
+
3.times do |activo|
|
|
477
|
+
p_prev = precios.get(t, activo)
|
|
478
|
+
p_curr = precios.get(t + 1, activo)
|
|
479
|
+
retornos_data << ((p_curr - p_prev) / p_prev)
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
retornos = GRX.tensor(retornos_data, [4, 3])
|
|
483
|
+
|
|
484
|
+
medias = Array.new(3) do |activo|
|
|
485
|
+
col = 4.times.map { |d| retornos.get(d, activo) }
|
|
486
|
+
col.sum / 4.0
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
centrado = []
|
|
490
|
+
4.times do |d|
|
|
491
|
+
3.times do |activo|
|
|
492
|
+
centrado << (retornos.get(d, activo) - medias[activo])
|
|
493
|
+
end
|
|
494
|
+
end
|
|
495
|
+
retornos_centrados = GRX.tensor(centrado, [4, 3])
|
|
496
|
+
|
|
497
|
+
covarianza = (retornos_centrados.t @ retornos_centrados) / 3.0
|
|
498
|
+
|
|
499
|
+
pesos = GRX.tensor([[0.4, 0.3, 0.3]], [1, 3])
|
|
500
|
+
var_portafolio = (pesos @ covarianza @ pesos.t).item
|
|
501
|
+
volatilidad = Math.sqrt(var_portafolio)
|
|
502
|
+
|
|
503
|
+
puts "Volatilidad Diaria del Portafolio: #{(volatilidad * 100).round(4)}%"
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
### C. Fisica de Particulas y Simulacion N-Cuerpos
|
|
509
|
+
|
|
510
|
+
Simula posiciones, velocidades y distancias euclidianas matriciales en 3D:
|
|
511
|
+
|
|
512
|
+
```ruby
|
|
513
|
+
require "grx"
|
|
514
|
+
|
|
515
|
+
n_particulas = 4
|
|
516
|
+
dt = 0.01
|
|
517
|
+
|
|
518
|
+
posiciones = GRX.tensor([
|
|
519
|
+
0.0, 0.0, 0.0,
|
|
520
|
+
1.0, 0.0, 0.0,
|
|
521
|
+
0.0, 1.0, 0.0,
|
|
522
|
+
0.0, 0.0, 1.0
|
|
523
|
+
], [n_particulas, 3])
|
|
524
|
+
|
|
525
|
+
velocidades = GRX.tensor([
|
|
526
|
+
0.1, 0.0, 0.0,
|
|
527
|
+
0.0, 0.2, 0.0,
|
|
528
|
+
0.0, 0.0, 0.1,
|
|
529
|
+
-0.1, 0.0, 0.0
|
|
530
|
+
], [n_particulas, 3])
|
|
531
|
+
|
|
532
|
+
gravedad = GRX.tensor(Array.new(n_particulas * 3) { |i| (i % 3 == 1) ? -9.81 : 0.0 }, [n_particulas, 3])
|
|
533
|
+
|
|
534
|
+
velocidades = velocidades + (gravedad * dt)
|
|
535
|
+
posiciones = posiciones + (velocidades * dt)
|
|
536
|
+
|
|
537
|
+
distancias_data = []
|
|
538
|
+
n_particulas.times do |i|
|
|
539
|
+
n_particulas.times do |j|
|
|
540
|
+
dx = posiciones.get(i, 0) - posiciones.get(j, 0)
|
|
541
|
+
dy = posiciones.get(i, 1) - posiciones.get(j, 1)
|
|
542
|
+
dz = posiciones.get(i, 2) - posiciones.get(j, 2)
|
|
543
|
+
distancias_data << Math.sqrt(dx*dx + dy*dy + dz*dz)
|
|
544
|
+
end
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
distancias = GRX.tensor(distancias_data, [n_particulas, n_particulas])
|
|
548
|
+
puts "Distancia entre Particulas P0 y P1: #{distancias.get(0, 1).round(4)}"
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
### D. Optimizacion Matematica Pura con Autograd (Funcion Rosenbrock)
|
|
554
|
+
|
|
555
|
+
Encuentra el minimo global de la funcion no convexa de Rosenbrock:
|
|
556
|
+
$$f(x, y) = (a - x)^2 + b(y - x^2)^2 \quad \text{con } a=1, b=100$$
|
|
557
|
+
|
|
558
|
+
```ruby
|
|
559
|
+
require "grx"
|
|
560
|
+
|
|
561
|
+
punto = GRX.tensor([-1.5, 2.0], [2], requires_grad: true)
|
|
562
|
+
lr = 0.002
|
|
563
|
+
|
|
564
|
+
500.times do |paso|
|
|
565
|
+
x = punto.get(0)
|
|
566
|
+
y = punto.get(1)
|
|
567
|
+
|
|
568
|
+
tx = GRX.tensor([x], [1], requires_grad: true)
|
|
569
|
+
ty = GRX.tensor([y], [1], requires_grad: true)
|
|
570
|
+
|
|
571
|
+
t1 = (GRX.tensor([1.0], [1]) - tx).square
|
|
572
|
+
t2 = (ty - tx.square).square * 100.0
|
|
573
|
+
perdida = t1 + t2
|
|
574
|
+
perdida.backward
|
|
575
|
+
|
|
576
|
+
nuevo_x = x - lr * tx.grad.item
|
|
577
|
+
nuevo_y = y - lr * ty.grad.item
|
|
578
|
+
punto = GRX.tensor([nuevo_x, nuevo_y], [2])
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
puts "Minimo Convergido: x = #{punto.get(0).round(3)}, y = #{punto.get(1).round(3)}"
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
---
|
|
585
|
+
|
|
586
|
+
## Recetario de Deep Learning (10 Arquitecturas de Redes Neuronales)
|
|
587
|
+
|
|
588
|
+
### Arquitectura 1: Clasificador de Vision y Caracteres (BatchNorm + Dropout)
|
|
589
|
+
|
|
590
|
+
```ruby
|
|
591
|
+
require "grx"
|
|
592
|
+
|
|
593
|
+
modelo_vision = GRX::NN::Sequential.new(
|
|
594
|
+
GRX::NN::Linear.new(784, 256),
|
|
595
|
+
GRX::NN::BatchNorm1d.new(256),
|
|
596
|
+
GRX::NN::ReLU.new,
|
|
597
|
+
GRX::NN::Dropout.new(0.25),
|
|
598
|
+
GRX::NN::Linear.new(256, 64),
|
|
599
|
+
GRX::NN::LayerNorm.new(64),
|
|
600
|
+
GRX::NN::LeakyReLU.new(alpha: 0.01),
|
|
601
|
+
GRX::NN::Linear.new(64, 10),
|
|
602
|
+
GRX::NN::Softmax.new
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
lote_imagenes = GRX.randn([16, 784])
|
|
606
|
+
predicciones = modelo_vision.forward(lote_imagenes) # Shape [16, 10]
|
|
607
|
+
puts "Predicciones de Vision Shape: #{predicciones.shape}"
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
---
|
|
611
|
+
|
|
612
|
+
### Arquitectura 2: NLP y Chatbot de Intenciones (Embedding + LayerNorm)
|
|
613
|
+
|
|
614
|
+
```ruby
|
|
615
|
+
require "grx"
|
|
616
|
+
|
|
617
|
+
vocab_size = 500
|
|
618
|
+
embedding_dim = 32
|
|
619
|
+
num_classes = 4
|
|
620
|
+
|
|
621
|
+
embedding = GRX::NN::Embedding.new(vocab_size, embedding_dim)
|
|
622
|
+
clasificador = GRX::NN::Sequential.new(
|
|
623
|
+
GRX::NN::LayerNorm.new(embedding_dim),
|
|
624
|
+
GRX::NN::Linear.new(embedding_dim, 16),
|
|
625
|
+
GRX::NN::Tanh.new,
|
|
626
|
+
GRX::NN::Linear.new(16, num_classes)
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
tokens = GRX.tensor([14, 2, 88, 412], [4])
|
|
630
|
+
palabras = embedding.forward(tokens)
|
|
631
|
+
|
|
632
|
+
vector_oracion = Array.new(embedding_dim) do |d|
|
|
633
|
+
4.times.sum { |t| palabras.get(t, d) } / 4.0
|
|
634
|
+
end
|
|
635
|
+
tensor_oracion = GRX.tensor(vector_oracion, [1, embedding_dim])
|
|
636
|
+
|
|
637
|
+
logits = clasificador.forward(tensor_oracion)
|
|
638
|
+
puts "Intencion Predicha: #{logits.argmax}"
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
---
|
|
642
|
+
|
|
643
|
+
### Arquitectura 3: Aprendizaje por Refuerzo (Agente Deep Q-Network DQN)
|
|
644
|
+
|
|
645
|
+
```ruby
|
|
646
|
+
require "grx"
|
|
647
|
+
|
|
648
|
+
dim_estado = 8
|
|
649
|
+
dim_accion = 4
|
|
650
|
+
|
|
651
|
+
q_net = GRX::NN::Sequential.new(
|
|
652
|
+
GRX::NN::Linear.new(dim_estado, 64),
|
|
653
|
+
GRX::NN::ReLU.new,
|
|
654
|
+
GRX::NN::Linear.new(64, 64),
|
|
655
|
+
GRX::NN::ReLU.new,
|
|
656
|
+
GRX::NN::Linear.new(64, dim_accion)
|
|
657
|
+
)
|
|
658
|
+
|
|
659
|
+
optimizador = GRX::Optim::Adam.new(q_net.parameters, lr: 0.001)
|
|
660
|
+
perdida_huber = GRX::Loss::HuberLoss.new(delta: 1.0)
|
|
661
|
+
|
|
662
|
+
estado_actual = GRX.randn([1, dim_estado])
|
|
663
|
+
q_objetivos = GRX.tensor([[1.2, 0.5, -0.8, 3.4]], [1, 4])
|
|
664
|
+
|
|
665
|
+
optimizador.zero_grad
|
|
666
|
+
q_predichos = q_net.forward(estado_actual)
|
|
667
|
+
loss = perdida_huber.call(q_predichos, q_objetivos)
|
|
668
|
+
loss.backward
|
|
669
|
+
optimizador.step
|
|
670
|
+
|
|
671
|
+
puts "Perdida Q: #{loss.item.round(6)}"
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
---
|
|
675
|
+
|
|
676
|
+
### Arquitectura 4: Pronostico No Lineal de Series Temporales Multivariables
|
|
677
|
+
|
|
678
|
+
```ruby
|
|
679
|
+
require "grx"
|
|
680
|
+
|
|
681
|
+
pronosticador = GRX::NN::Sequential.new(
|
|
682
|
+
GRX::NN::Linear.new(5, 32),
|
|
683
|
+
GRX::NN::Sigmoid.new,
|
|
684
|
+
GRX::NN::Linear.new(32, 16),
|
|
685
|
+
GRX::NN::ReLU.new,
|
|
686
|
+
GRX::NN::Linear.new(16, 1)
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
optimizador = GRX::Optim::Adam.new(pronosticador.parameters, lr: 0.01, weight_decay: 1e-4)
|
|
690
|
+
criterio = GRX::Loss::MSELoss.new
|
|
691
|
+
|
|
692
|
+
sensores = GRX.tensor([[22.5, 60.1, 1013.2, 5.4, 0.8]], [1, 5])
|
|
693
|
+
temp_esperada = GRX.tensor([[23.1]], [1, 1])
|
|
694
|
+
|
|
695
|
+
optimizador.zero_grad
|
|
696
|
+
pred = pronosticador.forward(sensores)
|
|
697
|
+
loss = criterio.call(pred, temp_esperada)
|
|
698
|
+
loss.backward
|
|
699
|
+
optimizador.step
|
|
700
|
+
|
|
701
|
+
puts "Error de Pronostico: #{loss.item.round(6)}"
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
---
|
|
705
|
+
|
|
706
|
+
### Arquitectura 5: Autoencoder Profundo para Reduccion Dimensional y Deteccion de Anomalias
|
|
707
|
+
|
|
708
|
+
Comprime vectores de alta dimension en un cuello de botella latente y reconstruye la entrada:
|
|
709
|
+
|
|
710
|
+
```ruby
|
|
711
|
+
require "grx"
|
|
712
|
+
|
|
713
|
+
# 1. Red Codificadora (Encoder): 64 entradas -> 16 ocultas -> 4 codigo latente
|
|
714
|
+
encoder = GRX::NN::Sequential.new(
|
|
715
|
+
GRX::NN::Linear.new(64, 16),
|
|
716
|
+
GRX::NN::LayerNorm.new(16),
|
|
717
|
+
GRX::NN::ReLU.new,
|
|
718
|
+
GRX::NN::Linear.new(16, 4)
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
# 2. Red Decodificadora (Decoder): 4 codigo latente -> 16 ocultas -> 64 salidas
|
|
722
|
+
decoder = GRX::NN::Sequential.new(
|
|
723
|
+
GRX::NN::Linear.new(4, 16),
|
|
724
|
+
GRX::NN::LayerNorm.new(16),
|
|
725
|
+
GRX::NN::ReLU.new,
|
|
726
|
+
GRX::NN::Linear.new(16, 64),
|
|
727
|
+
GRX::NN::Sigmoid.new
|
|
728
|
+
)
|
|
729
|
+
|
|
730
|
+
optimizador = GRX::Optim::Adam.new(encoder.parameters + decoder.parameters, lr: 0.01)
|
|
731
|
+
perdida_recon = GRX::Loss::MSELoss.new
|
|
732
|
+
|
|
733
|
+
lote = GRX.rand([8, 64])
|
|
734
|
+
|
|
735
|
+
optimizador.zero_grad
|
|
736
|
+
codigo_latente = encoder.forward(lote) # Shape [8, 4]
|
|
737
|
+
reconstruccion = decoder.forward(codigo_latente) # Shape [8, 64]
|
|
738
|
+
loss = perdida_recon.call(reconstruccion, lote)
|
|
739
|
+
loss.backward
|
|
740
|
+
optimizador.step
|
|
741
|
+
|
|
742
|
+
puts "Perdida de Reconstruccion del Autoencoder: #{loss.item.round(6)}"
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
---
|
|
746
|
+
|
|
747
|
+
### Arquitectura 6: Modelo Generador de Lenguaje y Siguiente Caracter Autoregresivo
|
|
748
|
+
|
|
749
|
+
Genera texto caracter a caracter mediante capas de incrustacion densa y cabezas lineales:
|
|
750
|
+
|
|
751
|
+
```ruby
|
|
752
|
+
require "grx"
|
|
753
|
+
|
|
754
|
+
tamano_vocabulario = 256 # Caracteres ASCII
|
|
755
|
+
dim_embedding = 16
|
|
756
|
+
longitud_contexto = 4 # Ventana de 4 caracteres
|
|
757
|
+
|
|
758
|
+
char_embedding = GRX::NN::Embedding.new(tamano_vocabulario, dim_embedding)
|
|
759
|
+
cabeza_idioma = GRX::NN::Sequential.new(
|
|
760
|
+
GRX::NN::Linear.new(dim_embedding * longitud_contexto, 64),
|
|
761
|
+
GRX::NN::LayerNorm.new(64),
|
|
762
|
+
GRX::NN::Tanh.new,
|
|
763
|
+
GRX::NN::Linear.new(64, tamano_vocabulario)
|
|
764
|
+
)
|
|
765
|
+
|
|
766
|
+
# Predecir siguiente caracter para contexto "hola" -> tokens [104, 111, 108, 97]
|
|
767
|
+
contexto_ids = GRX.tensor([104, 111, 108, 97], [4])
|
|
768
|
+
embebido = char_embedding.forward(contexto_ids) # Shape [4, 16]
|
|
769
|
+
contexto_plano = embebido.flatten.reshape([1, dim_embedding * longitud_contexto])
|
|
770
|
+
|
|
771
|
+
logits = cabeza_idioma.forward(contexto_plano) # Shape [1, 256]
|
|
772
|
+
siguiente_char_ascii = logits.argmax
|
|
773
|
+
|
|
774
|
+
puts "Contexto: 'hola' -> Siguiente Caracter Predicho: '#{siguiente_char_ascii.chr}' (ASCII #{siguiente_char_ascii})"
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
---
|
|
778
|
+
|
|
779
|
+
### Arquitectura 7: Analisis de Sentimiento y Clasificacion de Resenas (BCELoss)
|
|
780
|
+
|
|
781
|
+
Clasifica la polaridad de texto (Positivo / Negativo) a partir de secuencias de tokens:
|
|
782
|
+
|
|
783
|
+
```ruby
|
|
784
|
+
require "grx"
|
|
785
|
+
|
|
786
|
+
tamano_vocabulario = 1000
|
|
787
|
+
dim_embedding = 32
|
|
788
|
+
|
|
789
|
+
embedding_sentimiento = GRX::NN::Embedding.new(tamano_vocabulario, dim_embedding)
|
|
790
|
+
clasificador_sentimiento = GRX::NN::Sequential.new(
|
|
791
|
+
GRX::NN::LayerNorm.new(dim_embedding),
|
|
792
|
+
GRX::NN::Linear.new(dim_embedding, 16),
|
|
793
|
+
GRX::NN::ReLU.new,
|
|
794
|
+
GRX::NN::Dropout.new(0.2),
|
|
795
|
+
GRX::NN::Linear.new(16, 1),
|
|
796
|
+
GRX::NN::Sigmoid.new
|
|
797
|
+
)
|
|
798
|
+
|
|
799
|
+
# Resena tokenizada: "excelente producto entrega muy rapida" -> [42, 189, 7, 85, 301]
|
|
800
|
+
tokens_resena = GRX.tensor([42, 189, 7, 85, 301], [5])
|
|
801
|
+
vectores = embedding_sentimiento.forward(tokens_resena) # Shape [5, 32]
|
|
802
|
+
|
|
803
|
+
# Agregacion por promedio global de palabras
|
|
804
|
+
vector_resena = Array.new(dim_embedding) do |d|
|
|
805
|
+
5.times.sum { |t| vectores.get(t, d) } / 5.0
|
|
806
|
+
end
|
|
807
|
+
tensor_resena = GRX.tensor(vector_resena, [1, dim_embedding])
|
|
808
|
+
|
|
809
|
+
probabilidad_positiva = clasificador_sentimiento.forward(tensor_resena).item
|
|
810
|
+
etiqueta = probabilidad_positiva >= 0.5 ? "POSITIVO" : "NEGATIVO"
|
|
811
|
+
|
|
812
|
+
puts "Puntuacion de Sentimiento: #{(probabilidad_positiva * 100).round(2)}% -> #{etiqueta}"
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
---
|
|
816
|
+
|
|
817
|
+
### Arquitectura 8: Red Neuronal Siamesa para Verificacion de Similitud y Firmas
|
|
818
|
+
|
|
819
|
+
Ramas gemelas con pesos compartidos para verificacion biometrica o similitud semantica:
|
|
820
|
+
|
|
821
|
+
```ruby
|
|
822
|
+
require "grx"
|
|
823
|
+
|
|
824
|
+
extractor_caracteristicas = GRX::NN::Sequential.new(
|
|
825
|
+
GRX::NN::Linear.new(32, 16),
|
|
826
|
+
GRX::NN::LayerNorm.new(16),
|
|
827
|
+
GRX::NN::Tanh.new,
|
|
828
|
+
GRX::NN::Linear.new(16, 8)
|
|
829
|
+
)
|
|
830
|
+
|
|
831
|
+
# Dos firmas o imagenes de muestra
|
|
832
|
+
muestra_a = GRX.randn([1, 32])
|
|
833
|
+
muestra_b = GRX.randn([1, 32])
|
|
834
|
+
|
|
835
|
+
# Extraccion de vectores latentes con los mismos pesos compartidos
|
|
836
|
+
vector_a = extractor_caracteristicas.forward(muestra_a) # Shape [1, 8]
|
|
837
|
+
vector_b = extractor_caracteristicas.forward(muestra_b) # Shape [1, 8]
|
|
838
|
+
|
|
839
|
+
# Distancia euclidiana entre representaciones
|
|
840
|
+
diferencia = vector_a - vector_b
|
|
841
|
+
distancia = diferencia.square.sum.sqrt.item
|
|
842
|
+
|
|
843
|
+
resultado = distancia < 1.0 ? "COINCIDENCIA (Misma Entidad)" : "NO COINCIDEN (Distinta Entidad)"
|
|
844
|
+
puts "Distancia Latente: #{distancia.round(4)} -> #{resultado}"
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
---
|
|
848
|
+
|
|
849
|
+
### Arquitectura 9: Red Residual Profunda (Bloque ResNet MLP con Conexion Skip)
|
|
850
|
+
|
|
851
|
+
Permite entrenar redes ultra-profundas evitando el desvanecimiento del gradiente ($y = x + F(x)$):
|
|
852
|
+
|
|
853
|
+
```ruby
|
|
854
|
+
require "grx"
|
|
855
|
+
|
|
856
|
+
class BloqueResidual < GRX::NN::Module
|
|
857
|
+
def initialize(dim)
|
|
858
|
+
@fc1 = GRX::NN::Linear.new(dim, dim)
|
|
859
|
+
@bn1 = GRX::NN::BatchNorm1d.new(dim)
|
|
860
|
+
@act = GRX::NN::ReLU.new
|
|
861
|
+
@fc2 = GRX::NN::Linear.new(dim, dim)
|
|
862
|
+
@bn2 = GRX::NN::BatchNorm1d.new(dim)
|
|
863
|
+
end
|
|
864
|
+
|
|
865
|
+
def forward(x)
|
|
866
|
+
residual = x
|
|
867
|
+
out = @fc1.forward(x)
|
|
868
|
+
out = @bn1.forward(out)
|
|
869
|
+
out = @act.forward(out)
|
|
870
|
+
out = @fc2.forward(out)
|
|
871
|
+
out = @bn2.forward(out)
|
|
872
|
+
out + residual # Conexion de salto (Skip Connection)
|
|
873
|
+
end
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
bloque = BloqueResidual.new(16)
|
|
877
|
+
entrada = GRX.randn([4, 16])
|
|
878
|
+
salida_res = bloque.forward(entrada)
|
|
879
|
+
|
|
880
|
+
puts "Dimension de Salida del Bloque Residual: #{salida_res.shape}"
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
---
|
|
884
|
+
|
|
885
|
+
### Arquitectura 10: Filtrado Colaborativo Neuronal y Sistema de Recomendacion
|
|
886
|
+
|
|
887
|
+
Combina vectores de Usuarios e Items con capas densas para predecir afinidad de recomendacion:
|
|
888
|
+
|
|
889
|
+
```ruby
|
|
890
|
+
require "grx"
|
|
891
|
+
|
|
892
|
+
num_usuarios = 100
|
|
893
|
+
num_articulos = 50
|
|
894
|
+
dim_latente = 16
|
|
895
|
+
|
|
896
|
+
emb_usuario = GRX::NN::Embedding.new(num_usuarios, dim_latente)
|
|
897
|
+
emb_articulo = GRX::NN::Embedding.new(num_articulos, dim_latente)
|
|
898
|
+
|
|
899
|
+
red_afinidad = GRX::NN::Sequential.new(
|
|
900
|
+
GRX::NN::Linear.new(dim_latente * 2, 16),
|
|
901
|
+
GRX::NN::ReLU.new,
|
|
902
|
+
GRX::NN::Linear.new(16, 1),
|
|
903
|
+
GRX::NN::Sigmoid.new
|
|
904
|
+
)
|
|
905
|
+
|
|
906
|
+
# Predecir afinidad entre Usuario #7 y Pelicula #23
|
|
907
|
+
u_idx = GRX.tensor([7], [1])
|
|
908
|
+
i_idx = GRX.tensor([23], [1])
|
|
909
|
+
|
|
910
|
+
u_vec = emb_usuario.forward(u_idx) # Shape [1, 16]
|
|
911
|
+
i_vec = emb_articulo.forward(i_idx) # Shape [1, 16]
|
|
912
|
+
|
|
913
|
+
# Concatenar vectores -> Shape [1, 32]
|
|
914
|
+
interaccion = GRX.tensor(u_vec.to_a + i_vec.to_a, [1, dim_latente * 2])
|
|
915
|
+
puntuacion_predicha = red_afinidad.forward(interaccion).item
|
|
916
|
+
|
|
917
|
+
puts "Calificacion Estimada: #{(puntuacion_predicha * 5.0).round(2)} / 5.0 Estrellas"
|
|
918
|
+
```
|
|
919
|
+
|
|
920
|
+
---
|
|
921
|
+
|
|
922
|
+
## Funciones de Perdida (`GRX::Loss`)
|
|
923
|
+
|
|
924
|
+
Todas las funciones de perdida retornan un tensor escalar diferenciable listo para `loss.backward`.
|
|
925
|
+
|
|
926
|
+
| Clase de Perdida | Constructor y Parametros | Por Defecto | Explicacion Matematica y Uso |
|
|
927
|
+
|---|---|---|---|
|
|
928
|
+
| `GRX::Loss::MSELoss` | `new(reduction: :mean)` | `reduction: :mean` | Error Cuadratico Medio: $\frac{1}{N}\sum(y_{pred} - y_{true})^2$. Estandar en regresion continua. |
|
|
929
|
+
| `GRX::Loss::MAELoss` | `new(reduction: :mean)` | `reduction: :mean` | Error Absoluto Medio: $\frac{1}{N}\sum \|y_{pred} - y_{true}\|$. Robusto ante valores atipicos (*outliers*). |
|
|
930
|
+
| `GRX::Loss::BCELoss` | `new(reduction: :mean, eps: 1e-7)` | `eps: 1e-7` | Entropia Cruzada Binaria: $-[y \log(p) + (1-y) \log(1-p)]$. Protegido con `eps` para evitar $\log(0)$. |
|
|
931
|
+
| `GRX::Loss::CrossEntropyLoss` | `new(reduction: :mean)` | `reduction: :mean` | Entropia Cruzada Multiclase. Combina Softmax con Log-Sum-Exp y log-likelihood negativo. |
|
|
932
|
+
| `GRX::Loss::HuberLoss` | `new(delta: 1.0, reduction: :mean)` | `delta: 1.0` | Perdida Huber / Smooth L1: Cuadratica para error $< \delta$, lineal para error $\ge \delta$. |
|
|
933
|
+
|
|
934
|
+
* Opciones de `reduction`: `:mean` (divide la perdida total entre el tamano del lote; recomendado) o `:sum` (acumula la suma directa sin promediar).
|
|
935
|
+
|
|
936
|
+
---
|
|
937
|
+
|
|
938
|
+
## Guia de Notacion Cientifica y Parametros
|
|
939
|
+
|
|
940
|
+
### 1. Que significa la Notacion Cientifica en Machine Learning (`1e-1` a `1e-8`)?
|
|
941
|
+
|
|
942
|
+
En Ruby y en Inteligencia Artificial, los numeros muy pequenos se escriben en **notacion cientifica exponencial** (`1e-X` que equivale a $1.0 \times 10^{-X}$). Esto evita errores tipograficos al contar ceros decimales:
|
|
943
|
+
|
|
944
|
+
| Notacion Ruby | Valor Decimal Exacto | Fraccion | Nombre Comun | Uso Tipico en GRTensor |
|
|
945
|
+
|---|---|---|---|---|
|
|
946
|
+
| `1e-1` | `0.1` | $1/10$ | Una decima | Momentum de actualizacion en `BatchNorm1d`, learning rate rapido. |
|
|
947
|
+
| `1e-2` | `0.01` | $1/100$ | Una centesima | Learning rate estandar para `SGD`, pendiente `alpha` en `LeakyReLU`. |
|
|
948
|
+
| `1e-3` | `0.001` | $1/1,000$ | Una milesima | Learning rate estandar de oro para `Adam` en redes profundas. |
|
|
949
|
+
| `1e-4` | `0.0001` | $1/10,000$ | Una diezmilesima | Penalizacion de regularizacion L2 (`weight_decay`) para evitar sobreajuste. |
|
|
950
|
+
| `1e-5` | `0.00001` | $1/100,000$ | Una cienmilesima | Epsilon (`eps`) de varianza en `LayerNorm` y `BatchNorm1d`. |
|
|
951
|
+
| `1e-7` | `0.0000001` | $1/10,000,000$ | Una diezmillonesima | Epsilon de corte en `BCELoss` para evitar $\log(0) \to -\infty$. |
|
|
952
|
+
| `1e-8` | `0.00000001` | $1/100,000,000$ | Una cienmillonesima | Epsilon de estabilidad numerica en el denominador de `Adam`. |
|
|
953
|
+
|
|
954
|
+
---
|
|
955
|
+
|
|
956
|
+
### 2. Parametros de Capas Neuronales (`GRX::NN`)
|
|
957
|
+
|
|
958
|
+
| Capa / Modulo | Parametro | Tipo | Por Defecto | Rango Valido | Explicacion Didactica |
|
|
959
|
+
|---|---|---|---|---|---|
|
|
960
|
+
| `Linear` | `in_features` | Integer | (Requerido) | $\ge 1$ | Cantidad de numeros de entrada que recibe la capa. |
|
|
961
|
+
| `Linear` | `out_features` | Integer | (Requerido) | $\ge 1$ | Cantidad de neuronas o caracteristicas que produce de salida. |
|
|
962
|
+
| `Linear` | `bias` | Boolean | `true` | `true` / `false` | Si es `true`, agrega el termino de sesgo $b$ ($y = Wx + b$). |
|
|
963
|
+
| `Embedding`| `num_embeddings` | Integer | (Requerido) | $\ge 1$ | Tamano del vocabulario o numero total de entidades unicas. |
|
|
964
|
+
| `Embedding`| `embedding_dim` | Integer | (Requerido) | $\ge 1$ | Dimension del vector denso continuo para representar cada palabra. |
|
|
965
|
+
| `Dropout` | `p` | Float | `0.5` | `0.0` a `0.99` | Probabilidad de desactivar aleatoriamente una neurona durante el entrenamiento (0.2 = 20%, 0.5 = 50%) para evitar que la red se vuelva dependiente de una sola neurona. |
|
|
966
|
+
| `LeakyReLU`| `alpha` | Float | `0.01` | `0.001` a `0.3` | Pendiente para numeros negativos. Evita que las neuronas "mueran" permitiendo pasar un 1% de gradiente cuando $x < 0$. |
|
|
967
|
+
| `LayerNorm`| `normalized_shape`| Integer/Array | (Requerido) | Dimensiones | Dimension sobre la que se calcula la media y varianza unitaria. |
|
|
968
|
+
| `LayerNorm`| `eps` / `epsilon` | Float | `1e-5` | `1e-8` a `1e-4` | Termino sumado a la varianza para evitar division entre 0. |
|
|
969
|
+
| `BatchNorm1d`| `num_features` | Integer | (Requerido) | $\ge 1$ | Cantidad de canales a normalizar a lo largo del lote (*batch*). |
|
|
970
|
+
| `BatchNorm1d`| `eps` / `epsilon` | Float | `1e-5` | `1e-8` a `1e-4` | Termino de estabilidad sumado a la varianza por lote. |
|
|
971
|
+
| `BatchNorm1d`| `momentum` | Float | `0.1` | `0.01` a `0.5` | Factor de actualizacion de medias y varianzas moviles para inferencia. |
|
|
972
|
+
|
|
973
|
+
---
|
|
974
|
+
|
|
975
|
+
### 3. Parametros de Creacion y Operaciones de Tensores (`GRX::Tensor`)
|
|
976
|
+
|
|
977
|
+
| Factory / Metodo | Parametro | Tipo | Por Defecto | Descripcion |
|
|
978
|
+
|---|---|---|---|---|
|
|
979
|
+
| `GRX.tensor` | `data` | Array / Storage | (Requerido) | Arreglo plano o anidado de numeros Ruby (`[1.0, 2.0]` o `[[1, 2], [3, 4]]`). |
|
|
980
|
+
| `GRX.tensor` | `shape` | Array[Integer] | `nil` (Auto) | Dimensiones explicitas (ej. `[2, 3]`). Se infiere automaticamente si se omite. |
|
|
981
|
+
| `GRX.tensor` | `requires_grad`| Boolean | `false` | Habilita el rastreo de diferenciacion automatica (Autograd) en el DAG. |
|
|
982
|
+
| `GRX.zeros` / `GRX.ones` | `shape` | Array[Integer] | (Requerido) | Dimensiones del tensor a inicializar con `0.0` o `1.0`. |
|
|
983
|
+
| `GRX.rand` | `shape` | Array[Integer] | (Requerido) | Tensor aleatorio con distribucion uniforme $U[0, 1)$. |
|
|
984
|
+
| `GRX.randn` | `shape` | Array[Integer] | (Requerido) | Tensor aleatorio normal estandar $N(0, 1)$ mediante Box-Muller. |
|
|
985
|
+
| `Tensor.xavier_uniform`| `shape` | Array[Integer] | (Requerido) | Inicializacion Xavier/Glorot ($U[-\sqrt{6/(f_{in}+f_{out})}, \sqrt{6/(f_{in}+f_{out})}]$). |
|
|
986
|
+
| `Tensor.he_normal` | `shape` | Array[Integer] | (Requerido) | Inicializacion He/Kaiming normal ($N(0, \sqrt{2/f_{in}})$). Optima para capas ReLU. |
|
|
987
|
+
| `Tensor.zeros_like` | `other` | Tensor | (Requerido) | Crea un tensor de ceros con la misma forma que `other`. |
|
|
988
|
+
| `Tensor.ones_like` | `other` | Tensor | (Requerido) | Crea un tensor de unos con la misma forma que `other`. |
|
|
989
|
+
| `tensor.clip` | `lo`, `hi` | Numeric | (Requeridos) | Fija todos los elementos dentro del intervalo $[lo, hi]$. |
|
|
990
|
+
| `tensor.pow` | `exponent` | Numeric | (Requerido) | Eleva cada elemento a la potencia $x^e$. Totalmente diferenciable. |
|
|
991
|
+
| `tensor.reshape` | `new_shape` | Array[Integer] | (Requerido) | Cambia la forma manteniendo el conteo de elementos. Vista zero-copy. |
|
|
992
|
+
| `tensor.transpose` | (sin args) | - | - | Intercambia ejes de matrices 2D por zancadas. Vista zero-copy. |
|
|
993
|
+
| `tensor.flatten` | (sin args) | - | - | Aplana el tensor a 1 dimension `[numel]`. Vista zero-copy. |
|
|
994
|
+
| `tensor.contiguous`| (sin args) | - | - | Re-empaqueta vistas no contiguas en un buffer contiguo nuevo. |
|
|
995
|
+
| `tensor.get` | `*coords` | Integers | (Requerido) | Retorna el float escalar en las coordenadas indicadas. |
|
|
996
|
+
| `tensor.set` | `*coords, val` | Integers, Float | (Requeridos) | Modifica directamente el valor en las coordenadas indicadas. |
|
|
997
|
+
| `tensor.item` | (sin args) | - | - | Extrae el valor Float de un tensor escalar de 1 elemento. |
|
|
998
|
+
| `tensor.argmax` | (sin args) | - | - | Retorna el indice del elemento con el valor maximo. |
|
|
999
|
+
| `tensor.argmin` | (sin args) | - | - | Retorna el indice del elemento con el valor minimo. |
|
|
1000
|
+
| `tensor.backward` | `gradient` | Tensor | `nil` | Ejecuta la retropropagacion inversa a traves del grafo computacional DAG. |
|
|
1001
|
+
|
|
1002
|
+
---
|
|
1003
|
+
|
|
1004
|
+
### 4. Pipelines de Datos, Persistencia y Utilidades (`GRX::Data`, `GRX::Serialization`, `GRX::Utils`)
|
|
1005
|
+
|
|
1006
|
+
* `TensorDataset.new(*tensors)`:
|
|
1007
|
+
* `*tensors` (Requeridos): Tensores paralelos de caracteristicas y etiquetas que comparten el tamano de lote en dimension 0.
|
|
1008
|
+
* `DataLoader.new(dataset, batch_size: 32, shuffle: true)`:
|
|
1009
|
+
* `dataset` (`GRX::Data::Dataset`): Dataset envuelto.
|
|
1010
|
+
* `batch_size` (Integer, por defecto: `32`): Cantidad de muestras por lote.
|
|
1011
|
+
* `shuffle` (Boolean, por defecto: `true`): Permuta aleatoriamente los indices al inicio de cada epoca.
|
|
1012
|
+
* `GRX::Serialization.save(model, path)` / `model.save_weights(path)`:
|
|
1013
|
+
* `model` (`GRX::NN::Module`): Instancia del modelo neuronal.
|
|
1014
|
+
* `path` (String): Ruta del archivo `.grx` binario de salida. Vuelca directamente los doubles IEEE 754 de 64 bits.
|
|
1015
|
+
* `GRX::Serialization.load(model, path)` / `model.load_weights(path)`:
|
|
1016
|
+
* `model` (`GRX::NN::Module`): Modelo instanciado con la misma arquitectura.
|
|
1017
|
+
* `path` (String): Archivo `.grx` origen a cargar.
|
|
1018
|
+
* `model.train!` y `model.eval!`:
|
|
1019
|
+
* `train!`: Activa modo entrenamiento (habilita `Dropout` y calcula medias por lote en `BatchNorm1d`).
|
|
1020
|
+
* `eval!`: Activa modo inferencia (desactiva `Dropout` y congela medias fijas en `BatchNorm1d`).
|
|
1021
|
+
* `GRX::Utils.clip_grad_norm!(parameters, max_norm: 1.0)`:
|
|
1022
|
+
* `parameters` (Array[Tensor]): Coleccion de parametros con gradientes.
|
|
1023
|
+
* `max_norm` (Float, por defecto: `1.0`): Norma L2 maxima permitida para evitar explosiones.
|
|
1024
|
+
* `GRX::Utils.one_hot(indices, num_classes: nil, requires_grad: false)`:
|
|
1025
|
+
* `indices` (Array[Integer] o Tensor): Etiquetas de clase enteras (ej. `[0, 2, 1]`).
|
|
1026
|
+
* `num_classes` (Integer, opcional): Total de clases. Se calcula como `max + 1` si se omite.
|
|
1027
|
+
* `GRX.simd_mode`:
|
|
1028
|
+
* Consulta el nivel de vectorizacion nativa: `:avx2` (4 doubles/ciclo con FMA), `:sse` (2 doubles/ciclo), `:scalar` (C portable) o `:ruby`.
|
|
1029
|
+
|
|
1030
|
+
---
|
|
1031
|
+
|
|
1032
|
+
### 5. Jerarquia de Excepciones de GRTensor
|
|
1033
|
+
|
|
1034
|
+
| Excepcion | Hereda de | Causa Principal |
|
|
1035
|
+
|---|---|---|
|
|
1036
|
+
| `GRX::Error` | `StandardError` | Clase base para todas las excepciones del framework. |
|
|
1037
|
+
| `GRX::ShapeError` | `GRX::Error` | Dimensiones incompatibles en operaciones algebraicas o multiplicaciones de matrices. |
|
|
1038
|
+
| `GRX::DimensionError` | `GRX::Error` | Rango de dimensiones invalido (ej. ejecutar `transpose` o `matmul` en tensores 1D). |
|
|
1039
|
+
| `GRX::StorageError` | `GRX::Error` | Fallo de asignacion de memoria heap en C (OOM) o archivo binario `.grx` corrupto. |
|
|
1040
|
+
|
|
1041
|
+
---
|
|
1042
|
+
|
|
1043
|
+
## Catalogo de Optimizadores e Hiperparametros (`GRX::Optim`)
|
|
1044
|
+
|
|
1045
|
+
### 1. `GRX::Optim::Adam`
|
|
1046
|
+
Optimizador Adam (Adaptive Moment Estimation) vectorizado en C con instrucciones FMA y correccion de sesgo:
|
|
1047
|
+
|
|
1048
|
+
```ruby
|
|
1049
|
+
optimizer = GRX::Optim::Adam.new(
|
|
1050
|
+
modelo.parameters,
|
|
1051
|
+
lr: 0.001, # Tasa de aprendizaje (alpha). Recomendado: 1e-3 (0.001) para redes profundas
|
|
1052
|
+
betas: [0.9, 0.999], # [beta1, beta2] decaimiento exponencial para momentos de 1er/2do orden
|
|
1053
|
+
eps: 1e-8, # Epsilon para estabilidad numerica en el denominador (evita division entre 0)
|
|
1054
|
+
weight_decay: 1e-4 # Regularizacion L2 (penalizacion para encoger pesos y evitar sobreajuste)
|
|
1055
|
+
)
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
| Parametro | Tipo | Por Defecto | Rango Recomendado | Descripcion |
|
|
1059
|
+
|---|---|---|---|---|
|
|
1060
|
+
| `lr` | Float | `0.001` | `1e-4` a `1e-2` | Factor de escala del paso en la direccion opuesta al gradiente. |
|
|
1061
|
+
| `betas` / `beta1, beta2` | Array / Floats | `[0.9, 0.999]` | `[0.9, 0.999]` | $\beta_1$ conserva inercia de direccion; $\beta_2$ rastrea la varianza del gradiente al cuadrado. |
|
|
1062
|
+
| `eps` / `epsilon` | Float | `1e-8` | `1e-8` a `1e-6` | Constante diminuta sumada al denominador para prevenir `NaN`. |
|
|
1063
|
+
| `weight_decay` | Float | `0.0` | `1e-5` a `1e-3` | Penalizacion L2 ($\lambda$) que previene la memorizacion de datos (*overfitting*). |
|
|
1064
|
+
|
|
1065
|
+
### 2. `GRX::Optim::SGD`
|
|
1066
|
+
Descenso por Gradiente Estocastico con momento e inercia:
|
|
1067
|
+
|
|
1068
|
+
```ruby
|
|
1069
|
+
optimizer = GRX::Optim::SGD.new(
|
|
1070
|
+
modelo.parameters,
|
|
1071
|
+
lr: 0.01, # Tasa de aprendizaje. Recomendado: 0.01 a 0.1
|
|
1072
|
+
momentum: 0.9, # Coeficiente del buffer de momento (mu). Recomendado: 0.9
|
|
1073
|
+
weight_decay: 1e-4 # Factor de regularizacion L2
|
|
1074
|
+
)
|
|
1075
|
+
```
|
|
1076
|
+
|
|
1077
|
+
| Parametro | Tipo | Por Defecto | Rango Recomendado | Descripcion |
|
|
1078
|
+
|---|---|---|---|---|
|
|
1079
|
+
| `lr` | Float | `0.01` | `0.001` a `0.1` | Tamano del paso de descenso por gradiente. |
|
|
1080
|
+
| `momentum` | Float | `0.0` | `0.8` a `0.99` | Inercia acumulada para acelerar el descenso y filtrar oscilaciones caoticas. |
|
|
1081
|
+
| `weight_decay` | Float | `0.0` | `1e-5` a `1e-3` | Penalizacion L2 para regularizar pesos. |
|
|
1082
|
+
|
|
1083
|
+
---
|
|
1084
|
+
|
|
1085
|
+
## Pipelines de Datos y DataLoader (`GRX::Data`)
|
|
1086
|
+
|
|
1087
|
+
### 1. `GRX::Data::TensorDataset`
|
|
1088
|
+
Encapsula tensores de caracteristicas y etiquetas en un dataset indexable:
|
|
1089
|
+
|
|
1090
|
+
```ruby
|
|
1091
|
+
x_data = GRX.randn([1000, 20])
|
|
1092
|
+
y_data = GRX.tensor(Array.new(1000) { rand(0..2) }, [1000])
|
|
1093
|
+
|
|
1094
|
+
dataset = GRX::Data::TensorDataset.new(x_data, y_data)
|
|
1095
|
+
puts dataset.size # 1000 muestras
|
|
1096
|
+
```
|
|
1097
|
+
|
|
1098
|
+
### 2. `GRX::Data::DataLoader`
|
|
1099
|
+
Generador de lotes (*batches*) con barajado automatico e iteracion eficiente:
|
|
1100
|
+
|
|
1101
|
+
```ruby
|
|
1102
|
+
loader = GRX::Data::DataLoader.new(dataset, batch_size: 32, shuffle: true)
|
|
1103
|
+
|
|
1104
|
+
loader.each_with_index do |(batch_x, batch_y), idx|
|
|
1105
|
+
optimizer.zero_grad
|
|
1106
|
+
preds = modelo.forward(batch_x)
|
|
1107
|
+
loss = criterio.call(preds, batch_y)
|
|
1108
|
+
loss.backward
|
|
1109
|
+
optimizer.step
|
|
1110
|
+
end
|
|
1111
|
+
```
|
|
1112
|
+
|
|
1113
|
+
---
|
|
1114
|
+
|
|
1115
|
+
## Persistencia de Modelos y Cerebros (Formato `.grx`)
|
|
1116
|
+
|
|
1117
|
+
### Especificacion del Formato Binario `GRX1`
|
|
1118
|
+
|
|
1119
|
+
GRX incorpora un motor de serializacion binaria de alta velocidad que vuelca los parametros flotantes directamente desde la memoria nativa en C:
|
|
1120
|
+
|
|
1121
|
+
```text
|
|
1122
|
+
+-------------------+--------------------+---------------------------------------------+
|
|
1123
|
+
| Campo | Tamano | Contenido |
|
|
1124
|
+
+-------------------+--------------------+---------------------------------------------+
|
|
1125
|
+
| Cabecera Magica | 8 bytes | "GRX1\0\0\0\0" (ASCII con relleno nulo) |
|
|
1126
|
+
| Total Parametros | 4 bytes | uint32 big-endian |
|
|
1127
|
+
+-------------------+--------------------+---------------------------------------------+
|
|
1128
|
+
| Para cada tensor de parametros: |
|
|
1129
|
+
| - Rango | 2 bytes | uint16 big-endian |
|
|
1130
|
+
| - Dimensiones | Rango * 4 bytes | Arreglo uint32 big-endian |
|
|
1131
|
+
| - Numel | 8 bytes | uint64 big-endian |
|
|
1132
|
+
| - Carga de Datos | Numel * 8 bytes | Doubles IEEE 754 de 64 bits (Copia directa) |
|
|
1133
|
+
+-------------------+--------------------+---------------------------------------------+
|
|
1134
|
+
```
|
|
1135
|
+
|
|
1136
|
+
### Flujo de Despliegue de Inferencia en Produccion
|
|
1137
|
+
|
|
1138
|
+
#### Paso 1: Entrenar y Guardar el Cerebro (`entrenar.rb`)
|
|
1139
|
+
```ruby
|
|
1140
|
+
require "grx"
|
|
1141
|
+
|
|
1142
|
+
modelo = GRX::NN::Sequential.new(
|
|
1143
|
+
GRX::NN::Linear.new(4, 16),
|
|
1144
|
+
GRX::NN::ReLU.new,
|
|
1145
|
+
GRX::NN::Linear.new(16, 2)
|
|
1146
|
+
)
|
|
1147
|
+
|
|
1148
|
+
# ... (bucle de entrenamiento) ...
|
|
1149
|
+
|
|
1150
|
+
modelo.save_weights("cerebro_campeon.grx")
|
|
1151
|
+
puts "Pesos guardados exitosamente en cerebro_campeon.grx"
|
|
1152
|
+
```
|
|
1153
|
+
|
|
1154
|
+
#### Paso 2: Cargar e Inyectar en API de Produccion (`servidor_api.rb`)
|
|
1155
|
+
```ruby
|
|
1156
|
+
require "grx"
|
|
1157
|
+
|
|
1158
|
+
cerebro = GRX::NN::Sequential.new(
|
|
1159
|
+
GRX::NN::Linear.new(4, 16),
|
|
1160
|
+
GRX::NN::ReLU.new,
|
|
1161
|
+
GRX::NN::Linear.new(16, 2)
|
|
1162
|
+
)
|
|
1163
|
+
|
|
1164
|
+
# Cargar los pesos binarios al instante sin re-entrenar
|
|
1165
|
+
cerebro.load_weights("cerebro_campeon.grx")
|
|
1166
|
+
cerebro.eval!
|
|
1167
|
+
|
|
1168
|
+
peticion = GRX.tensor([[0.5, -1.2, 3.4, 0.1]], [1, 4])
|
|
1169
|
+
probabilidades = cerebro.forward(peticion).softmax.to_a
|
|
1170
|
+
|
|
1171
|
+
puts "Probabilidades de Decision en Produccion: #{probabilidades.map { |d| d.round(4) }}"
|
|
1172
|
+
```
|
|
1173
|
+
|
|
1174
|
+
---
|
|
1175
|
+
|
|
1176
|
+
## Gestion de Gradientes y Utilidades (`GRX::Utils`)
|
|
1177
|
+
|
|
1178
|
+
```ruby
|
|
1179
|
+
# 1. Recorte de norma de gradiente (Gradient Clipping) para estabilizar redes profundas
|
|
1180
|
+
norma_total = GRX::Utils.clip_grad_norm!(modelo.parameters, max_norm: 1.0)
|
|
1181
|
+
|
|
1182
|
+
# 2. Conversion de etiquetas a One-Hot
|
|
1183
|
+
one_hot_matriz = GRX::Utils.one_hot([0, 2, 1], num_classes: 3)
|
|
1184
|
+
```
|
|
1185
|
+
|
|
1186
|
+
---
|
|
1187
|
+
|
|
1188
|
+
## Guia de Soporte y Herramientas para Windows
|
|
1189
|
+
|
|
1190
|
+
En el sistema operativo Windows:
|
|
1191
|
+
* **Aceleracion C Nativa (Recomendado):** Se activa automaticamente al instalar con **RubyInstaller con DevKit (MSYS2 / MinGW-w64)**. La extension nativa se compila de manera transparente durante `gem install grx-tensor`.
|
|
1192
|
+
* **Fallback en Ruby Puro:** Si DevKit no esta instalado en el sistema, GRX conmuta de forma segura al modo de calculo en Ruby puro sin producir errores.
|
|
1193
|
+
* **Binarios Pre-compilados Independientes:** Las gemas fat-binary con archivos `.dll` pre-empaquetados se encuentran en desarrollo activo.
|
|
1194
|
+
|
|
1195
|
+
---
|
|
1196
|
+
|
|
1197
|
+
## Licencia
|
|
1198
|
+
|
|
1199
|
+
Licencia MIT. Copyright (c) 2026 Razo.
|