grx-tensor 0.1.0 → 0.2.0
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 +17 -0
- data/GUIA_PRINCIPIANTES.md +763 -0
- data/README.es.md +270 -0
- data/README.md +178 -375
- data/grx-tensor.gemspec +4 -2
- data/lib/grx/c_api.rb +41 -37
- data/lib/grx/data.rb +87 -0
- data/lib/grx/loss.rb +55 -25
- data/lib/grx/nn.rb +153 -36
- data/lib/grx/optim.rb +9 -9
- data/lib/grx/serialization.rb +66 -0
- data/lib/grx/storage.rb +15 -24
- data/lib/grx/tensor.rb +198 -65
- data/lib/grx/utils.rb +28 -0
- data/lib/grx/version.rb +1 -1
- data/lib/grx.rb +14 -3
- metadata +8 -3
|
@@ -0,0 +1,763 @@
|
|
|
1
|
+
# Guia de GRX-Tensor para Principiantes (Desde Cero a Produccion)
|
|
2
|
+
|
|
3
|
+
**Aprende computacion tensorial, autograd, redes neuronales, chatbots con memoria, NLP, datasets masivos y despliegue en sistemas reales paso a paso.**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Tabla de Contenido
|
|
8
|
+
|
|
9
|
+
1. [Bienvenida y Filosofia del Framework](#1-bienvenida-y-filosofia-del-framework)
|
|
10
|
+
2. [Fundamentos: Que es un Shape, Numel, Strides y Rango?](#2-fundamentos-que-es-un-shape-numel-strides-y-rango)
|
|
11
|
+
- [De Escalar a Tensor Multidimensional](#de-escalar-a-tensor-multidimensional)
|
|
12
|
+
- [Como se guardan los datos en memoria real (C vs Ruby)](#como-se-guardan-los-datos-en-memoria-real-c-vs-ruby)
|
|
13
|
+
- [La formula matematica del indice plano](#la-formula-matematica-del-indice-plano)
|
|
14
|
+
3. [Margen de Error y Precision Numerica en GRX](#3-margen-de-error-y-precision-numerica-en-grx)
|
|
15
|
+
- [Doble precision IEEE 754 (Float 64-bit)](#doble-precision-ieee-754-float-64-bit)
|
|
16
|
+
- [Que es FMA (Fused Multiply-Add) y por que reduce el error?](#que-es-fma-fused-multiply-add-y-por-que-reduce-el-error)
|
|
17
|
+
- [Tolerancias (Epsilon) y medicion real del error](#tolerancias-epsilon-y-medicion-real-del-error)
|
|
18
|
+
4. [Procesamiento de Lenguaje Natural (NLP): Tokenizacion y Chatbots](#4-procesamiento-de-lenguaje-natural-nlp-tokenizacion-y-chatbots)
|
|
19
|
+
- [Que es la Tokenizacion y como se hace?](#que-es-la-tokenizacion-y-como-se-hace)
|
|
20
|
+
- [De Tokens a Vectores Semanticos (Embedding)](#de-tokens-a-vectores-semanticos-embedding)
|
|
21
|
+
- [Como crear un modelo que hable contigo y use su memoria?](#como-crear-un-modelo-que-hable-contigo-y-use-su-memoria)
|
|
22
|
+
- [Control de Confianza: Que pasa cuando el bot no entiende?](#control-de-confianza-que-pasa-cuando-el-bot-no-entiende)
|
|
23
|
+
5. [Como Usar Modelos Entrenados en Produccion (Sin Reentrenar)](#5-como-usar-modelos-entrenados-en-produccion-sin-reentrenar)
|
|
24
|
+
- [El Flujo de Vida: Entrenamiento vs Inferencia](#el-flujo-de-vida-entrenamiento-vs-inferencia)
|
|
25
|
+
- [Ejemplo de API Web con Sinatra / Rails en Finanzas](#ejemplo-de-api-web-con-sinatra--rails-en-finanzas)
|
|
26
|
+
6. [Primeros Pasos: Creando Tensores](#6-primeros-pasos-creando-tensores)
|
|
27
|
+
7. [Aritmetica y Operaciones Basicas](#7-aritmetica-y-operaciones-basicas)
|
|
28
|
+
8. [Algebra Lineal Intuitiva: Multiplicacion de Matrices](#8-algebra-lineal-intuitiva-multiplicacion-de-matrices)
|
|
29
|
+
9. [El Superpoder de GRX: Que es Autograd?](#9-el-superpoder-de-grx-que-es-autograd)
|
|
30
|
+
10. [Construyendo Redes Neuronales Bloque a Bloque](#10-construyendo-redes-neuronales-bloque-a-bloque)
|
|
31
|
+
11. [El Ciclo Sagrado del Entrenamiento (Con loss.backward directo)](#11-el-ciclo-sagrado-del-entrenamiento-con-lossbackward-directo)
|
|
32
|
+
12. [Proyectos Guiados Paso a Paso (Completos y Ejecutables)](#12-proyectos-guiados-paso-a-paso-completos-y-ejecutables)
|
|
33
|
+
- [Proyecto 1: El Predictor de Formulas (Regresion Lineal)](#proyecto-1-el-predictor-de-formulas-regresion-lineal)
|
|
34
|
+
- [Proyecto 2: Ajuste de Curvas No Lineales y Guardado en .grx](#proyecto-2-ajuste-de-curvas-no-lineales-y-guardado-en-grx)
|
|
35
|
+
- [Proyecto 3: Clasificador Binario Inteligente con BCELoss](#proyecto-3-clasificador-binario-inteligente-con-bceloss)
|
|
36
|
+
- [Proyecto 4: Entrenamiento con Datasets Masivos (5,000 Filas con DataLoader)](#proyecto-4-entrenamiento-con-datasets-masivos-5000-filas-con-dataloader)
|
|
37
|
+
- [Proyecto 5: Chatbot Financiero Inteligente con Base de Conocimiento y Filtro de Incertidumbre](#proyecto-5-chatbot-financiero-inteligente-con-base-de-conocimiento-y-filtro-de-incertidumbre)
|
|
38
|
+
13. [Buenas Practicas y Errores Comunes](#13-buenas-practicas-y-errores-comunes)
|
|
39
|
+
14. [Glosario de Terminos Clave](#14-glosario-de-terminos-clave)
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 1. Bienvenida y Filosofia del Framework
|
|
44
|
+
|
|
45
|
+
Ruby es un lenguaje celebrado por su elegancia y legibilidad. Sin embargo, en el procesamiento de matrices masivas e inteligencia artificial, la sobrecarga del recolector de basura (GC) puede restar velocidad si todo se hace en Ruby puro.
|
|
46
|
+
|
|
47
|
+
Por otro lado, C ofrece velocidad extrema, pero programar grafos de autograd y redes neuronales en C directamente es propenso a errores de memoria.
|
|
48
|
+
|
|
49
|
+
**GRX-Tensor une lo mejor de ambos mundos:**
|
|
50
|
+
- **Ruby habla:** Escribes codigo fluido, declarativo y limpio para definir arquitecturas, procesar datos y crear aplicaciones.
|
|
51
|
+
- **C calcula:** Cada operacion matematica, producto matricial, retropropagacion y actualizacion de pesos se ejecuta en un buffer nativo de C con aceleracion vectorial **SIMD (AVX2 + FMA)**, procesando 4 numeros de doble precision por ciclo de CPU.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 2. Fundamentos: Que es un Shape, Numel, Strides y Rango?
|
|
56
|
+
|
|
57
|
+
### De Escalar a Tensor Multidimensional
|
|
58
|
+
|
|
59
|
+
Matematicamente, un tensor es una estructura de datos que generaliza escalares, vectores y matrices a cualquier numero de dimensiones:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
Rango 0 (Escalar):
|
|
63
|
+
Un solo valor numerico.
|
|
64
|
+
Shape: [1]
|
|
65
|
+
Ejemplo: 42.0
|
|
66
|
+
|
|
67
|
+
Rango 1 (Vector):
|
|
68
|
+
Una secuencia unidimensional de longitud N.
|
|
69
|
+
Shape: [3]
|
|
70
|
+
Visual: [ 1.0, 2.0, 3.0 ]
|
|
71
|
+
|
|
72
|
+
Rango 2 (Matriz):
|
|
73
|
+
Una tabla bidimensional de Filas x Columnas.
|
|
74
|
+
Shape: [2, 3] (2 filas, 3 columnas)
|
|
75
|
+
Visual:
|
|
76
|
+
┌ ┐
|
|
77
|
+
│ 1.0 2.0 3.0 │ <- Fila 0
|
|
78
|
+
│ 4.0 5.0 6.0 │ <- Fila 1
|
|
79
|
+
└ ┘
|
|
80
|
+
|
|
81
|
+
Rango 3 (Tensor 3D):
|
|
82
|
+
Un bloque tridimensional (Profundidad x Filas x Columnas).
|
|
83
|
+
Shape: [2, 2, 3] (2 matrices de 2x3)
|
|
84
|
+
|
|
85
|
+
Rango 4 (Tensor 4D):
|
|
86
|
+
Comun en procesamiento de imagenes y batches: [Batch, Canales, Alto, Ancho].
|
|
87
|
+
Shape: [64, 3, 224, 224] (64 imagenes RGB de 224x224 pixeles)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### Conceptos Clave
|
|
93
|
+
|
|
94
|
+
#### 1. Shape (Forma dimensional)
|
|
95
|
+
Es el arreglo de enteros que describe el tamano de cada dimension del tensor.
|
|
96
|
+
- Ejemplo: `[5000, 4]` representa una tabla de 5,000 filas y 4 columnas de caracteristicas.
|
|
97
|
+
|
|
98
|
+
#### 2. Numel (Numero total de elementos)
|
|
99
|
+
Es la cantidad total de valores escalares que contiene el tensor:
|
|
100
|
+
|
|
101
|
+
$$\text{numel} = \prod_{i=0}^{d-1} \text{shape}[i]$$
|
|
102
|
+
|
|
103
|
+
En Ruby:
|
|
104
|
+
```ruby
|
|
105
|
+
t = GRX.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3])
|
|
106
|
+
puts t.numel # => 6 (porque 2 * 3 = 6)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### 3. Strides (Saltos de memoria)
|
|
110
|
+
Indica cuantos elementos de la memoria plana contigua se deben saltar para avanzar una posicion en cada eje:
|
|
111
|
+
- Para `shape = [2, 3]`, los strides contiguos son `[3, 1]`:
|
|
112
|
+
- Para avanzar 1 columna a la derecha: saltas **1** posicion en memoria.
|
|
113
|
+
- Para avanzar 1 fila hacia abajo: saltas **3** posiciones en memoria.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
### Como se guardan los datos en memoria real (C vs Ruby)
|
|
118
|
+
|
|
119
|
+
En la memoria fisica (RAM), los datos existen en una sola linea recta de bytes contiguos:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
Buffer plano en memoria C:
|
|
123
|
+
Posicion C: [0] [1] [2] [3] [4] [5]
|
|
124
|
+
Valor: 1.0 2.0 3.0 4.0 5.0 6.0
|
|
125
|
+
|
|
126
|
+
Interpretacion logica 2D (Shape [2, 3]):
|
|
127
|
+
Fila 0: 1.0 2.0 3.0
|
|
128
|
+
Fila 1: 4.0 5.0 6.0
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### La formula matematica del indice plano
|
|
132
|
+
|
|
133
|
+
$$\text{Indice Plano} = \text{offset} + (\text{fila} \times \text{stride}_0) + (\text{columna} \times \text{stride}_1)$$
|
|
134
|
+
|
|
135
|
+
Para acceder a `(fila: 1, columna: 1)` con `strides = [3, 1]`:
|
|
136
|
+
$$\text{Indice Plano} = 0 + (1 \times 3) + (1 \times 1) = 4 \implies \text{Valor } 5.0$$
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 3. Margen de Error y Precision Numerica en GRX
|
|
141
|
+
|
|
142
|
+
### Doble precision IEEE 754 (Float 64-bit)
|
|
143
|
+
|
|
144
|
+
A diferencia de librerias que usan 32 bits (`float32`), GRX utiliza **doble precision de 64 bits (`double`)** en todo su nucleo de C:
|
|
145
|
+
|
|
146
|
+
| Formato | Bits Totales | Bits de Mantisa | Digitos Decimales Significativos | Margen de Error por Redondeo |
|
|
147
|
+
|---|---|---|---|---|
|
|
148
|
+
| `float32` | 32 bits | 24 bits | ~7 digitos | $\approx 1 \times 10^{-7}$ |
|
|
149
|
+
| `double` (GRX) | 64 bits | 53 bits | **~15 a 17 digitos** | $\mathbf{\approx 2 \times 10^{-16}}$ |
|
|
150
|
+
|
|
151
|
+
### FMA (Fused Multiply-Add)
|
|
152
|
+
En el producto punto (`dot`), multiplicacion de matrices (`matmul`) y optimizador `Adam`, se calcula $a \times b + c$:
|
|
153
|
+
- **Sin FMA:** Se multiplica $a \times b$, se redondea el resultado, se suma $c$ y se vuelve a redondear.
|
|
154
|
+
- **Con FMA en GRX:** La CPU calcula $a \times b + c$ con precision infinita intermedia y hace **un solo redondeo final**, reduciendo a la mitad el margen de error acumulado.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 4. Procesamiento de Lenguaje Natural (NLP): Tokenizacion y Chatbots
|
|
159
|
+
|
|
160
|
+
Una computadora no puede multiplicar palabras directamente como `"prestamo"` o `"saldo"`. Para que una red neuronal pueda procesar texto, comprender intenciones y responder con lenguaje natural, se sigue un pipeline matematico estricto:
|
|
161
|
+
|
|
162
|
+
### Que es la Tokenizacion y como se hace?
|
|
163
|
+
|
|
164
|
+
La tokenizacion es el proceso de convertir un texto en una secuencia ordenada de numeros enteros (*tokens*):
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
Frase del usuario:
|
|
168
|
+
"cual es mi saldo disponible hoy"
|
|
169
|
+
│
|
|
170
|
+
▼
|
|
171
|
+
1. Limpieza y separacion en palabras (split):
|
|
172
|
+
["cual", "es", "mi", "saldo", "disponible", "hoy"]
|
|
173
|
+
│
|
|
174
|
+
▼
|
|
175
|
+
2. Diccionario de Vocabulario (mapeo de palabra a ID entero):
|
|
176
|
+
{ "cual" => 1, "es" => 2, "mi" => 3, "saldo" => 4, "disponible" => 5, "hoy" => 6 }
|
|
177
|
+
│
|
|
178
|
+
▼
|
|
179
|
+
3. Arreglo de IDs de entrada:
|
|
180
|
+
[1, 2, 3, 4, 5, 6]
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
En Ruby:
|
|
184
|
+
```ruby
|
|
185
|
+
vocab = ["cual", "es", "mi", "saldo", "disponible", "hoy"]
|
|
186
|
+
token_to_id = vocab.each_with_index.to_h
|
|
187
|
+
|
|
188
|
+
mensaje = "mi saldo disponible"
|
|
189
|
+
ids = mensaje.split.map { |w| token_to_id[w] || 0 }
|
|
190
|
+
# => [3, 4, 5]
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
### De Tokens a Vectores Semanticos (`Embedding`)
|
|
196
|
+
|
|
197
|
+
Un simple numero entero como `4` no contiene significado de lo que es un `"saldo"`. Por ello, la capa `GRX::NN::Embedding` traduce cada ID a un vector denso de punto flotante en un espacio continuo de $D$ dimensiones (ej. 16 dimensiones):
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
Token 4 ("saldo") ───> [ 0.42, -0.18, 0.95, ..., -0.04 ] (16 floats)
|
|
201
|
+
Token 5 ("dinero") ──> [ 0.40, -0.15, 0.91, ..., -0.02 ] (Muy similar!)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Durante el entrenamiento, palabras con significados similares terminan con vectores muy cercanos en el espacio vectorial.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
### Como crear un modelo que hable contigo y use su memoria?
|
|
209
|
+
|
|
210
|
+
Para crear un agente conversacional que entienda lo que le pides y recupere informacion de su base de conocimiento:
|
|
211
|
+
|
|
212
|
+
1. **Base de Conocimiento (Memoria de Hechos):** Se define un diccionario con las respuestas correctas para cada intencion de negocio (saldo, tasas, prestamos, horarios).
|
|
213
|
+
2. **Clasificador de Intenciones Neuronal:** La red procesa la frase mediante `Embedding -> LayerNorm -> Linear -> ReLU -> Linear -> Softmax` y calcula probabilidades para cada intencion.
|
|
214
|
+
3. **Recuperacion de Memoria:** Al identificar la intencion con mayor probabilidad, el bot extrae la respuesta precisa de su memoria.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
### Control de Confianza: Que pasa cuando el bot no entiende?
|
|
219
|
+
|
|
220
|
+
Si un usuario escribe algo fuera de dominio (ej. *"como hago una pizza"*):
|
|
221
|
+
- **Verificacion de Cobertura de Vocabulario:** Si la mayoria de las palabras son desconocidas para el modelo ($< 50\%$ de cobertura), se rechaza inmediatamente.
|
|
222
|
+
- **Filtro de Probabilidad Softmax (*Confidence Threshold*):** Si la certeza calculada por la red es menor al $70\%$, el bot responde de forma segura:
|
|
223
|
+
> *"Disculpa, no comprendo tu mensaje con suficiente certeza. Solo puedo responder sobre saldo, prestamos o tasas de interes."*
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 5. Como Usar Modelos Entrenados en Produccion (Sin Reentrenar)
|
|
228
|
+
|
|
229
|
+
Una duda muy comun es: *Si entreno una red para finanzas o clasificacion, como la uso en un sistema real (API web, Rails, Sinatra o app de escritorio) sin tener que reentrenarla cada vez?*
|
|
230
|
+
|
|
231
|
+
### El Flujo de Vida: Entrenamiento vs Inferencia
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
235
|
+
│ FASE 1: ENTRENAMIENTO (Se hace 1 sola vez) │
|
|
236
|
+
│ 1. Lees tu dataset historico (5,000+ filas). │
|
|
237
|
+
│ 2. Entrenas la red con DataLoader y Adam. │
|
|
238
|
+
│ 3. Guardas los pesos: modelo.save_weights("finanzas.grx") │
|
|
239
|
+
│ 4. Guardas el vocabulario / normalizacion en un JSON. │
|
|
240
|
+
└──────────────────────────────┬──────────────────────────────┘
|
|
241
|
+
│
|
|
242
|
+
Genera el archivo "finanzas.grx"
|
|
243
|
+
│
|
|
244
|
+
▼
|
|
245
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
246
|
+
│ FASE 2: PRODUCCION / INFERENCIA (En tu API) │
|
|
247
|
+
│ 1. Al arrancar el servidor web (Rails/Sinatra): │
|
|
248
|
+
│ - Instancias la arquitectura del modelo. │
|
|
249
|
+
│ - Cargas los pesos: modelo.load_weights("finanzas.grx") │
|
|
250
|
+
│ - Pones la red en modo inferencia: modelo.eval! │
|
|
251
|
+
│ 2. Al recibir una peticion HTTP POST: │
|
|
252
|
+
│ - Conviertes el JSON de entrada a un Tensor. │
|
|
253
|
+
│ - Ejecutas pred = modelo.call(tensor_entrada). │
|
|
254
|
+
│ - Retornas la respuesta en < 0.1 milisegundos! │
|
|
255
|
+
└─────────────────────────────────────────────────────────────┘
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Ejemplo de API Web con Sinatra / Rails en Finanzas
|
|
259
|
+
|
|
260
|
+
```ruby
|
|
261
|
+
# app_api.rb — Microservicio de evaluacion de credito en tiempo real
|
|
262
|
+
require "grx"
|
|
263
|
+
require "json"
|
|
264
|
+
|
|
265
|
+
# 1. Cargar el modelo en memoria al iniciar la aplicacion (Toma < 1 ms)
|
|
266
|
+
MODELO_RIESGO = GRX::NN::Sequential.new(
|
|
267
|
+
GRX::NN::Linear.new(4, 16),
|
|
268
|
+
GRX::NN::LayerNorm.new(16),
|
|
269
|
+
GRX::NN::Tanh.new,
|
|
270
|
+
GRX::NN::Linear.new(16, 1),
|
|
271
|
+
GRX::NN::Sigmoid.new
|
|
272
|
+
)
|
|
273
|
+
MODELO_RIESGO.load_weights("modelo_riesgo_crediticio.grx")
|
|
274
|
+
MODELO_RIESGO.eval!
|
|
275
|
+
|
|
276
|
+
# 2. Endpoint de inferencia para evaluar solicitudes de credito
|
|
277
|
+
def evaluar_solicitud(ingresos_mensuales, deuda_actual, score_buro, monto_solicitado)
|
|
278
|
+
# Normalizar datos (utilizando las medias y desviaciones del entrenamiento)
|
|
279
|
+
x1 = (ingresos_mensuales - 25000.0) / 10000.0
|
|
280
|
+
x2 = (deuda_actual - 5000.0) / 4000.0
|
|
281
|
+
x3 = (score_buro - 650.0) / 100.0
|
|
282
|
+
x4 = (monto_solicitado - 50000.0) / 30000.0
|
|
283
|
+
|
|
284
|
+
entrada = GRX.tensor([x1, x2, x3, x4], [1, 4])
|
|
285
|
+
probabilidad_aprobacion = MODELO_RIESGO.call(entrada).to_a[0]
|
|
286
|
+
|
|
287
|
+
decision = probabilidad_aprobacion >= 0.70 ? "APROBADO" : "RECHAZADO"
|
|
288
|
+
|
|
289
|
+
{
|
|
290
|
+
decision: decision,
|
|
291
|
+
confianza: (probabilidad_aprobacion * 100).round(2),
|
|
292
|
+
tasa_sugerida: decision == "APROBADO" ? "12.5% anual" : "N/A"
|
|
293
|
+
}
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Prueba en produccion:
|
|
297
|
+
# res = evaluar_solicitud(45000.0, 2000.0, 720.0, 80000.0)
|
|
298
|
+
# puts res.inspect
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## 6. Primeros Pasos: Creando Tensores
|
|
304
|
+
|
|
305
|
+
```ruby
|
|
306
|
+
require "grx"
|
|
307
|
+
|
|
308
|
+
# 1. Vector de 3 elementos
|
|
309
|
+
v = GRX.tensor([1.0, 2.0, 3.0], [3])
|
|
310
|
+
puts "Shape: #{v.shape}" # => [3]
|
|
311
|
+
puts "Numel: #{v.numel}" # => 3
|
|
312
|
+
|
|
313
|
+
# 2. Matriz de 2 filas x 3 columnas
|
|
314
|
+
m = GRX.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3])
|
|
315
|
+
puts "Matriz: #{m.shape}" # => [2, 3]
|
|
316
|
+
|
|
317
|
+
# 3. Factorias
|
|
318
|
+
ceros = GRX.zeros([2, 2]) # Matriz 2x2 de 0.0
|
|
319
|
+
unos = GRX.ones([3, 1]) # Matriz 3x1 de 1.0
|
|
320
|
+
|
|
321
|
+
# 4. Obtener un valor escalar
|
|
322
|
+
scalar = GRX.tensor([99.0], [1])
|
|
323
|
+
puts scalar.item # => 99.0 (Float)
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
## 7. Aritmetica y Operaciones Basicas
|
|
329
|
+
|
|
330
|
+
```ruby
|
|
331
|
+
require "grx"
|
|
332
|
+
|
|
333
|
+
a = GRX.tensor([10.0, 20.0, 30.0], [3])
|
|
334
|
+
b = GRX.tensor([2.0, 5.0, 10.0], [3])
|
|
335
|
+
|
|
336
|
+
# Operaciones tensor-tensor (C + SIMD)
|
|
337
|
+
puts (a + b).to_a # => [12.0, 25.0, 40.0]
|
|
338
|
+
puts (a * b).to_a # => [20.0, 100.0, 300.0]
|
|
339
|
+
|
|
340
|
+
# Operaciones con escalares
|
|
341
|
+
puts (a + 5.0).to_a # => [15.0, 25.0, 35.0]
|
|
342
|
+
puts (2.0 * a).to_a # => [20.0, 40.0, 60.0]
|
|
343
|
+
|
|
344
|
+
# Reducciones diferenciables
|
|
345
|
+
x = GRX.tensor([1.0, 4.0, 9.0, 16.0], [4])
|
|
346
|
+
puts x.sum.item # => 30.0
|
|
347
|
+
puts x.mean.item # => 7.5
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## 8. Algebra Lineal Intuitiva: Multiplicacion de Matrices
|
|
353
|
+
|
|
354
|
+
```ruby
|
|
355
|
+
require "grx"
|
|
356
|
+
|
|
357
|
+
# [2, 3] x [3, 2] -> [2, 2]
|
|
358
|
+
a = GRX.tensor([1.0, 2.0, 3.0,
|
|
359
|
+
4.0, 5.0, 6.0], [2, 3])
|
|
360
|
+
|
|
361
|
+
b = GRX.tensor([7.0, 8.0,
|
|
362
|
+
9.0, 10.0,
|
|
363
|
+
11.0, 12.0], [3, 2])
|
|
364
|
+
|
|
365
|
+
c = a.matmul(b)
|
|
366
|
+
puts c.shape # => [2, 2]
|
|
367
|
+
puts c.to_a # => [58.0, 64.0, 139.0, 154.0]
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
## 9. El Superpoder de GRX: Que es Autograd?
|
|
373
|
+
|
|
374
|
+
```ruby
|
|
375
|
+
require "grx"
|
|
376
|
+
|
|
377
|
+
x = GRX.tensor([2.0, 3.0], [2], requires_grad: true)
|
|
378
|
+
y = GRX.tensor([4.0, 5.0], [2], requires_grad: true)
|
|
379
|
+
|
|
380
|
+
# Computamos z = (x + y) * y
|
|
381
|
+
z = ((x + y) * y).sum
|
|
382
|
+
z.backward
|
|
383
|
+
|
|
384
|
+
# Derivadas exactas calculadas automaticamente
|
|
385
|
+
puts x.grad.to_a # => [4.0, 5.0]
|
|
386
|
+
puts y.grad.to_a # => [10.0, 13.0]
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## 10. Construyendo Redes Neuronales Bloque a Bloque
|
|
392
|
+
|
|
393
|
+
```ruby
|
|
394
|
+
require "grx"
|
|
395
|
+
|
|
396
|
+
modelo = GRX::NN::Sequential.new(
|
|
397
|
+
GRX::NN::Linear.new(4, 16),
|
|
398
|
+
GRX::NN::LayerNorm.new(16),
|
|
399
|
+
GRX::NN::ReLU.new,
|
|
400
|
+
GRX::NN::Linear.new(16, 1)
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
puts modelo
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
408
|
+
## 11. El Ciclo Sagrado del Entrenamiento (Con `loss.backward` directo)
|
|
409
|
+
|
|
410
|
+
```ruby
|
|
411
|
+
opt.zero_grad # 1. Limpiar gradientes anteriores
|
|
412
|
+
pred = modelo.call(train_x) # 2. Forward pass
|
|
413
|
+
loss = loss_fn.call(pred, train_y) # 3. Calcular la perdida
|
|
414
|
+
loss.backward # 4. Backward pass automatico
|
|
415
|
+
opt.step # 5. Ajustar pesos
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
---
|
|
419
|
+
|
|
420
|
+
## 12. Proyectos Guiados Paso a Paso (Completos y Ejecutables)
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
### Proyecto 1: El Predictor de Formulas (Regresion Lineal)
|
|
425
|
+
|
|
426
|
+
Aprende la relacion $y = 3x + 2$ usando `loss.backward` nativo.
|
|
427
|
+
|
|
428
|
+
```ruby
|
|
429
|
+
require "grx"
|
|
430
|
+
|
|
431
|
+
train_x = GRX.tensor([1.0, 2.0, 3.0, 4.0, 5.0], [5, 1])
|
|
432
|
+
train_y = GRX.tensor([5.0, 8.0, 11.0, 14.0, 17.0], [5, 1])
|
|
433
|
+
|
|
434
|
+
modelo = GRX::NN::Sequential.new(
|
|
435
|
+
GRX::NN::Linear.new(1, 1)
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
opt = GRX::Optim::Adam.new(modelo.parameters, lr: 0.1)
|
|
439
|
+
loss_fn = GRX::Loss::MSELoss.new
|
|
440
|
+
|
|
441
|
+
150.times do
|
|
442
|
+
opt.zero_grad
|
|
443
|
+
pred = modelo.call(train_x)
|
|
444
|
+
loss = loss_fn.call(pred, train_y)
|
|
445
|
+
loss.backward
|
|
446
|
+
opt.step
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
test_x = GRX.tensor([6.0], [1, 1])
|
|
450
|
+
puts "Prediccion para x=6 (esperado=20): #{modelo.call(test_x).to_a[0].round(3)}"
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
### Proyecto 2: Ajuste de Curvas No Lineales y Guardado en `.grx`
|
|
456
|
+
|
|
457
|
+
```ruby
|
|
458
|
+
require "grx"
|
|
459
|
+
|
|
460
|
+
train_x = GRX.tensor((1..10).map(&:to_f), [10, 1])
|
|
461
|
+
train_y = GRX.tensor((1..10).map { |x| 2.0 * x + 1.0 }, [10, 1])
|
|
462
|
+
|
|
463
|
+
mlp = GRX::NN::Sequential.new(
|
|
464
|
+
GRX::NN::Linear.new(1, 16),
|
|
465
|
+
GRX::NN::LayerNorm.new(16),
|
|
466
|
+
GRX::NN::Tanh.new,
|
|
467
|
+
GRX::NN::Linear.new(16, 1)
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
opt = GRX::Optim::Adam.new(mlp.parameters, lr: 0.05)
|
|
471
|
+
loss_fn = GRX::Loss::MSELoss.new
|
|
472
|
+
|
|
473
|
+
300.times do
|
|
474
|
+
opt.zero_grad
|
|
475
|
+
pred = mlp.call(train_x)
|
|
476
|
+
loss = loss_fn.call(pred, train_y)
|
|
477
|
+
loss.backward
|
|
478
|
+
GRX::Utils.clip_grad_norm!(mlp.parameters, 1.0)
|
|
479
|
+
opt.step
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
# Guardar pesos a disco
|
|
483
|
+
mlp.save_weights("/tmp/mlp_entrenado.grx")
|
|
484
|
+
|
|
485
|
+
# Cargar en una nueva instancia limpia para inferencia
|
|
486
|
+
modelo_nuevo = GRX::NN::Sequential.new(
|
|
487
|
+
GRX::NN::Linear.new(1, 16),
|
|
488
|
+
GRX::NN::LayerNorm.new(16),
|
|
489
|
+
GRX::NN::Tanh.new,
|
|
490
|
+
GRX::NN::Linear.new(16, 1)
|
|
491
|
+
)
|
|
492
|
+
modelo_nuevo.load_weights("/tmp/mlp_entrenado.grx")
|
|
493
|
+
|
|
494
|
+
puts "Inferencia x=11: #{modelo_nuevo.call(GRX.tensor([11.0], [1, 1])).to_a[0].round(3)} (Esperado: 23.0)"
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
---
|
|
498
|
+
|
|
499
|
+
### Proyecto 3: Clasificador Binario Inteligente con BCELoss
|
|
500
|
+
|
|
501
|
+
```ruby
|
|
502
|
+
require "grx"
|
|
503
|
+
|
|
504
|
+
train_x = GRX.tensor([0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0, 1.0], [4, 2])
|
|
505
|
+
train_y = GRX.tensor([0.0, 1.0, 1.0, 1.0], [4, 1])
|
|
506
|
+
|
|
507
|
+
clasificador = GRX::NN::Sequential.new(
|
|
508
|
+
GRX::NN::Linear.new(2, 4),
|
|
509
|
+
GRX::NN::Tanh.new,
|
|
510
|
+
GRX::NN::Linear.new(4, 1),
|
|
511
|
+
GRX::NN::Sigmoid.new
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
opt = GRX::Optim::Adam.new(clasificador.parameters, lr: 0.08)
|
|
515
|
+
loss_fn = GRX::Loss::BCELoss.new
|
|
516
|
+
|
|
517
|
+
400.times do
|
|
518
|
+
opt.zero_grad
|
|
519
|
+
pred = clasificador.call(train_x)
|
|
520
|
+
loss = loss_fn.call(pred, train_y)
|
|
521
|
+
loss.backward
|
|
522
|
+
opt.step
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
puts "Resultados OR: #{clasificador.call(train_x).to_a.map { |v| v.round(4) }}"
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
530
|
+
### Proyecto 4: Entrenamiento con Datasets Masivos (5,000 Filas con DataLoader)
|
|
531
|
+
|
|
532
|
+
```ruby
|
|
533
|
+
require "grx"
|
|
534
|
+
|
|
535
|
+
num_samples = 5000
|
|
536
|
+
num_features = 4
|
|
537
|
+
|
|
538
|
+
# y = 2*x1 - 3*x2 + 1.5*x3 - 0.5*x4 + 4.0
|
|
539
|
+
raw_x = Array.new(num_samples * num_features) { rand * 2.0 - 1.0 }
|
|
540
|
+
raw_y = Array.new(num_samples) do |i|
|
|
541
|
+
x1, x2, x3, x4 = raw_x.slice(i * 4, 4)
|
|
542
|
+
2.0 * x1 - 3.0 * x2 + 1.5 * x3 - 0.5 * x4 + 4.0 + (rand - 0.5) * 0.02
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
train_dataset = GRX::Data::TensorDataset.new(
|
|
546
|
+
GRX.tensor(raw_x[0...(4000*4)], [4000, 4]),
|
|
547
|
+
GRX.tensor(raw_y[0...4000], [4000, 1])
|
|
548
|
+
)
|
|
549
|
+
val_x = GRX.tensor(raw_x[(4000*4)..], [1000, 4])
|
|
550
|
+
val_y = GRX.tensor(raw_y[4000..], [1000, 1])
|
|
551
|
+
|
|
552
|
+
train_loader = GRX::Data::DataLoader.new(train_dataset, batch_size: 64, shuffle: true)
|
|
553
|
+
|
|
554
|
+
model = GRX::NN::Sequential.new(
|
|
555
|
+
GRX::NN::Linear.new(4, 16),
|
|
556
|
+
GRX::NN::LayerNorm.new(16),
|
|
557
|
+
GRX::NN::Tanh.new,
|
|
558
|
+
GRX::NN::Linear.new(16, 1)
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
opt = GRX::Optim::Adam.new(model.parameters, lr: 0.03)
|
|
562
|
+
loss_fn = GRX::Loss::MSELoss.new
|
|
563
|
+
|
|
564
|
+
20.times do |epoch|
|
|
565
|
+
train_loader.each do |bx, by|
|
|
566
|
+
opt.zero_grad
|
|
567
|
+
pred = model.call(bx)
|
|
568
|
+
loss = loss_fn.call(pred, by)
|
|
569
|
+
loss.backward
|
|
570
|
+
opt.step
|
|
571
|
+
end
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
val_loss = loss_fn.call(model.call(val_x), val_y).item
|
|
575
|
+
puts "Error de Validacion MSE (5,000 muestras): #{val_loss.round(6)}"
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
---
|
|
579
|
+
|
|
580
|
+
### Proyecto 5: Chatbot Financiero Inteligente con Base de Conocimiento y Filtro de Incertidumbre
|
|
581
|
+
|
|
582
|
+
Este proyecto crea un agente de atencion al cliente que comprende intenciones mediante redes neuronales y recupera respuestas exactas de su memoria. Si una consulta es incomprensible o fuera de tema, lo reconoce honestamente:
|
|
583
|
+
|
|
584
|
+
```ruby
|
|
585
|
+
require "grx"
|
|
586
|
+
|
|
587
|
+
# 1. Base de conocimiento (Memoria del asistente)
|
|
588
|
+
KNOWLEDGE_BASE = {
|
|
589
|
+
"saludo" => [
|
|
590
|
+
"Hola, soy tu asistente financiero. En que puedo ayudarte hoy?",
|
|
591
|
+
"Que tal! Estoy listo para responder tus dudas financieras y de cuenta."
|
|
592
|
+
],
|
|
593
|
+
"consultar_saldo" => [
|
|
594
|
+
"Tu saldo disponible actual es de $14,850.50 MXN en tu cuenta principal.",
|
|
595
|
+
"Actualmente cuentas con $14,850.50 MXN listos para operar."
|
|
596
|
+
],
|
|
597
|
+
"tasa_interes" => [
|
|
598
|
+
"Nuestra tasa de rendimiento anual fija actual es del 11.5% anual.",
|
|
599
|
+
"La tasa de interes en cuentas de inversion es de 11.5% anual con pago mensual."
|
|
600
|
+
],
|
|
601
|
+
"prestamo" => [
|
|
602
|
+
"Puedes solicitar un prestamo de hasta $100,000 MXN directamente desde la app.",
|
|
603
|
+
"Para prestamos personales, ofrecemos plazos de 6 a 36 meses con aprobacion inmediata."
|
|
604
|
+
],
|
|
605
|
+
"despedida" => [
|
|
606
|
+
"Hasta luego! Que tengas un excelente dia.",
|
|
607
|
+
"Nos vemos, vuelve pronto si necesitas mas informacion."
|
|
608
|
+
]
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
# 2. Frases de entrenamiento
|
|
612
|
+
training_phrases = [
|
|
613
|
+
["hola buenos dias", "saludo"],
|
|
614
|
+
["hola que tal", "saludo"],
|
|
615
|
+
["buenas tardes", "saludo"],
|
|
616
|
+
["cuanto dinero tengo en mi cuenta", "consultar_saldo"],
|
|
617
|
+
["cual es mi saldo disponible hoy", "consultar_saldo"],
|
|
618
|
+
["cuanto saldo me queda", "consultar_saldo"],
|
|
619
|
+
["que tasa de interes tienen", "tasa_interes"],
|
|
620
|
+
["cuanto rendimiento me da la inversion", "tasa_interes"],
|
|
621
|
+
["como puedo pedir un prestamo", "prestamo"],
|
|
622
|
+
["necesito un credito personal", "prestamo"],
|
|
623
|
+
["quiero solicitar un prestamo", "prestamo"],
|
|
624
|
+
["adios muchas gracias por todo", "despedida"],
|
|
625
|
+
["hasta luego nos vemos pronto", "despedida"]
|
|
626
|
+
]
|
|
627
|
+
|
|
628
|
+
# 3. Tokenizacion y vocabulario
|
|
629
|
+
intents = KNOWLEDGE_BASE.keys
|
|
630
|
+
intent_to_id = intents.each_with_index.to_h
|
|
631
|
+
id_to_intent = intent_to_id.invert
|
|
632
|
+
num_classes = intents.size
|
|
633
|
+
|
|
634
|
+
vocab = training_phrases.flat_map { |text, _| text.split }.uniq
|
|
635
|
+
token_to_id = vocab.each_with_index.to_h
|
|
636
|
+
vocab_size = vocab.size
|
|
637
|
+
embedding_dim = 16
|
|
638
|
+
seq_len = 6
|
|
639
|
+
|
|
640
|
+
batch_size = training_phrases.size
|
|
641
|
+
x_ids = training_phrases.map do |text, _|
|
|
642
|
+
tokens = text.split.map { |w| token_to_id[w] }
|
|
643
|
+
(tokens + [0] * seq_len).first(seq_len)
|
|
644
|
+
end
|
|
645
|
+
train_x = GRX.tensor(x_ids.flatten.map(&:to_f), [batch_size, seq_len])
|
|
646
|
+
|
|
647
|
+
onehot = Array.new(batch_size * num_classes, 0.0)
|
|
648
|
+
training_phrases.each_with_index { |(_, label), i| onehot[i * num_classes + intent_to_id[label]] = 1.0 }
|
|
649
|
+
train_y = GRX.tensor(onehot, [batch_size, num_classes])
|
|
650
|
+
|
|
651
|
+
# 4. Arquitectura de la Red
|
|
652
|
+
emb = GRX::NN::Embedding.new(vocab_size, embedding_dim)
|
|
653
|
+
ln = GRX::NN::LayerNorm.new(embedding_dim)
|
|
654
|
+
fc1 = GRX::NN::Linear.new(embedding_dim, 16)
|
|
655
|
+
act = GRX::NN::ReLU.new
|
|
656
|
+
fc2 = GRX::NN::Linear.new(16, num_classes)
|
|
657
|
+
|
|
658
|
+
params = emb.parameters + ln.parameters + fc1.parameters + fc2.parameters
|
|
659
|
+
opt = GRX::Optim::Adam.new(params, lr: 0.05)
|
|
660
|
+
loss_fn = GRX::Loss::CrossEntropyLoss.new
|
|
661
|
+
|
|
662
|
+
# 5. Entrenamiento
|
|
663
|
+
150.times do
|
|
664
|
+
opt.zero_grad
|
|
665
|
+
flat = train_x.flatten
|
|
666
|
+
e = emb.call(flat)
|
|
667
|
+
e_data = e.to_a
|
|
668
|
+
pooled_data = Array.new(batch_size * embedding_dim, 0.0)
|
|
669
|
+
batch_size.times do |b|
|
|
670
|
+
embedding_dim.times do |d|
|
|
671
|
+
s = 0.0
|
|
672
|
+
seq_len.times { |t| s += e_data[(b * seq_len + t) * embedding_dim + d] }
|
|
673
|
+
pooled_data[b * embedding_dim + d] = s / seq_len.to_f
|
|
674
|
+
end
|
|
675
|
+
end
|
|
676
|
+
pooled = GRX.tensor(pooled_data, [batch_size, embedding_dim], requires_grad: true)
|
|
677
|
+
logits = fc2.call(act.call(fc1.call(ln.call(pooled))))
|
|
678
|
+
loss = loss_fn.call(logits, train_y)
|
|
679
|
+
loss.backward
|
|
680
|
+
|
|
681
|
+
if pooled.grad
|
|
682
|
+
grad_emb = Array.new(batch_size * seq_len * embedding_dim, 0.0)
|
|
683
|
+
p_grad = pooled.grad.to_a
|
|
684
|
+
batch_size.times do |b|
|
|
685
|
+
embedding_dim.times do |d|
|
|
686
|
+
v = p_grad[b * embedding_dim + d] / seq_len.to_f
|
|
687
|
+
seq_len.times { |t| grad_emb[(b * seq_len + t) * embedding_dim + d] = v }
|
|
688
|
+
end
|
|
689
|
+
end
|
|
690
|
+
e.backward(GRX.tensor(grad_emb, e.shape))
|
|
691
|
+
end
|
|
692
|
+
opt.step
|
|
693
|
+
end
|
|
694
|
+
|
|
695
|
+
# 6. Motor de conversacion con control de confianza
|
|
696
|
+
def chatear(mensaje, emb, ln, fc1, act, fc2, token_to_id, id_to_intent, seq_len, embedding_dim)
|
|
697
|
+
words = mensaje.downcase.gsub(/[^a-z0-9\s]/, "").split
|
|
698
|
+
known_count = words.count { |w| token_to_id.key?(w) }
|
|
699
|
+
coverage = words.empty? ? 0.0 : known_count.to_f / words.size
|
|
700
|
+
|
|
701
|
+
# Si la mayoria de las palabras son desconocidas, rechazar amablemente
|
|
702
|
+
if coverage < 0.40
|
|
703
|
+
return "Bot: Disculpa, no comprendo tu mensaje. Solo puedo responder sobre saldo, prestamos o tasas."
|
|
704
|
+
end
|
|
705
|
+
|
|
706
|
+
tokens = words.map { |w| token_to_id[w] || 0 }
|
|
707
|
+
padded = (tokens + [0] * seq_len).first(seq_len)
|
|
708
|
+
t_in = GRX.tensor(padded.map(&:to_f), [seq_len])
|
|
709
|
+
e = emb.call(t_in)
|
|
710
|
+
e_data = e.to_a
|
|
711
|
+
|
|
712
|
+
m_data = Array.new(embedding_dim) do |d|
|
|
713
|
+
seq_len.times.sum { |t| e_data[t * embedding_dim + d] } / seq_len.to_f
|
|
714
|
+
end
|
|
715
|
+
p_t = GRX.tensor(m_data, [1, embedding_dim])
|
|
716
|
+
logits = fc2.call(act.call(fc1.call(ln.call(p_t))))
|
|
717
|
+
probs = logits.softmax.to_a
|
|
718
|
+
|
|
719
|
+
best_prob, best_idx = probs.each_with_index.max_by { |p, idx| p }
|
|
720
|
+
|
|
721
|
+
if best_prob < 0.70
|
|
722
|
+
"Bot: No estoy seguro de haberte entendido (#{(best_prob * 100).round(1)}% certeza). Podrias reformular?"
|
|
723
|
+
else
|
|
724
|
+
intent = id_to_intent[best_idx]
|
|
725
|
+
respuesta = KNOWLEDGE_BASE[intent].sample
|
|
726
|
+
"Bot: #{respuesta} (Certeza: #{(best_prob * 100).round(1)}%)"
|
|
727
|
+
end
|
|
728
|
+
end
|
|
729
|
+
|
|
730
|
+
# 7. Prueba interactiva
|
|
731
|
+
puts "\n--- Simulacion de Chat ---"
|
|
732
|
+
[
|
|
733
|
+
"Hola muy buenos dias",
|
|
734
|
+
"Oye cuanto dinero tengo en mi cuenta",
|
|
735
|
+
"Que tasa de interes tienen actualmente",
|
|
736
|
+
"Quiero pedir un prestamo personal",
|
|
737
|
+
"Como puedo cocinar una hamburguesa", # Fuera de tema
|
|
738
|
+
"Adios muchas gracias"
|
|
739
|
+
].each do |msg|
|
|
740
|
+
puts "\nUsuario: #{msg}"
|
|
741
|
+
puts chatear(msg, emb, ln, fc1, act, fc2, token_to_id, id_to_intent, seq_len, embedding_dim)
|
|
742
|
+
end
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
---
|
|
746
|
+
|
|
747
|
+
## 13. Buenas Practicas y Errores Comunes
|
|
748
|
+
|
|
749
|
+
1. **Llamar `opt.zero_grad` en cada iteracion:** Evita que los gradientes se acumulen indefinidamente.
|
|
750
|
+
2. **Dimensiones compatibles:** Las capas lineales esperan siempre tensores 2D `[batch_size, num_features]`.
|
|
751
|
+
3. **Manejar `train!` y `eval!`:** Modos necesarios para capas como `Dropout` y `BatchNorm1d` durante inferencia.
|
|
752
|
+
|
|
753
|
+
---
|
|
754
|
+
|
|
755
|
+
## 14. Glosario de Terminos Clave
|
|
756
|
+
|
|
757
|
+
- **Shape:** Dimensiones del tensor (ej. `[10, 4]` -> 10 filas, 4 columnas).
|
|
758
|
+
- **Numel:** Total de elementos del tensor.
|
|
759
|
+
- **Strides:** Saltos de memoria plana para avanzar en cada eje.
|
|
760
|
+
- **Tokenizacion:** Division y conversion de texto a identificadores enteros.
|
|
761
|
+
- **Embedding:** Capa que transforma tokens en vectores densos continuos.
|
|
762
|
+
- **CrossEntropyLoss:** Perdida multiclase para clasificar texto e intenciones.
|
|
763
|
+
- **FMA:** Fused Multiply-Add en CPU que calcula $a \times b + c$ en 1 solo ciclo con minima desviacion.
|