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.
data/README.es.md ADDED
@@ -0,0 +1,270 @@
1
+ # GRX-Tensor
2
+
3
+ **Ruby habla. C calcula.**
4
+
5
+ Un framework de tensores para Ruby con diferenciacion automatica (autograd), un nucleo de computo en C con instrucciones vectoriales SIMD, y primitivas completas para redes neuronales, todo respaldado por una API de Ruby limpia y expresiva.
6
+
7
+ [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%203.0-CC342D?logo=ruby)](https://www.ruby-lang.org)
8
+ [![Licencia: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE.txt)
9
+ [![Plataforma](https://img.shields.io/badge/platform-Linux%20%7C%20macOS%20%7C%20Windows-lightgrey)](https://github.com/Gabo-Razo/grx-tensor)
10
+
11
+ ---
12
+
13
+ ## Que es GRX?
14
+
15
+ GRX es una biblioteca de computacion tensorial de alto rendimiento para Ruby. Su nucleo numerico esta implementado en C y compilado con extensiones **AVX2 + FMA** SIMD — procesando 4 numeros de punto flotante de doble precision (doubles) por ciclo de reloj con multiplicacion-suma fusionada.
16
+
17
+ Ruby gestiona la interfaz de usuario de alto nivel: validacion de formas dimensionales (shapes), construccion del grafo de computacion y orquestacion. C maneja la memoria nativa y las operaciones aritmeticas pesadas.
18
+
19
+ ### Caracteristicas clave
20
+
21
+ | Caracteristica | Detalle |
22
+ |---|---|
23
+ | **Nucleo C+SIMD** | AVX2+FMA, soporte SSE2 y fallback escalar autodetectado al compilar |
24
+ | **Memoria Alineada** | Asignacion en el heap de C alineada a 32 bytes (`posix_memalign` / `_aligned_malloc`) |
25
+ | **Autograd** | Diferenciacion automatica con funciones de perdida 100% diferenciables |
26
+ | **Optimizadores** | SGD (con momento y weight decay) y Adam (vectorizado en C con FMA) |
27
+ | **Capas de Redes Neuronales** | Linear, Sequential, Embedding, LayerNorm, Dropout, BatchNorm1d |
28
+ | **Funciones de Activacion** | ReLU, Leaky ReLU, Tanh, Sigmoid, Softmax (todas diferenciables) |
29
+ | **Funciones de Perdida** | MSE, MAE, BCE, CrossEntropy, Huber (totalmente diferenciables) |
30
+ | **Persistencia de Modelos** | Formato binario nativo de alta velocidad `.grx` (`save_weights` / `load_weights`) |
31
+ | **Pipelines de Datos** | `TensorDataset` y `DataLoader` para procesamiento por lotes (*batching*) y barajado (*shuffle*) |
32
+ | **Inicializacion de Pesos** | Xavier uniforme, He normal (Box-Muller en C) |
33
+ | **Multiplataforma** | Linux (`.so`), macOS (`.dylib`), Windows (`.dll`) |
34
+ | **Fallback Ruby Puro** | Funciona sin compilador de C en caso de que los binarios no esten disponibles |
35
+
36
+ ---
37
+
38
+ ## Instalacion
39
+
40
+ ### Mediante RubyGems
41
+
42
+ ```bash
43
+ gem install grx-tensor
44
+ ```
45
+
46
+ O agregalo a tu `Gemfile`:
47
+
48
+ ```ruby
49
+ gem "grx-tensor"
50
+ ```
51
+
52
+ ### Compilacion Manual desde el Codigo Fuente
53
+
54
+ Si clonas el repositorio directamente, compila la extension nativa:
55
+
56
+ **Linux / macOS:**
57
+ ```bash
58
+ make -C ext/unix
59
+ ```
60
+
61
+ **Windows (MinGW-w64):**
62
+ ```bash
63
+ make -C ext/windows -f Makefile.mingw
64
+ ```
65
+
66
+ ---
67
+
68
+ ## Inicio Rapido
69
+
70
+ ```ruby
71
+ require "grx"
72
+
73
+ # 1. Crear tensores con seguimiento de gradientes
74
+ a = GRX.tensor([1.0, 2.0, 3.0], [3], requires_grad: true)
75
+ b = GRX.tensor([4.0, 5.0, 6.0], [3], requires_grad: true)
76
+
77
+ # 2. Realizar operaciones aritmeticas (ejecutadas en C con SIMD)
78
+ c = (a * b) + 2.0
79
+
80
+ # 3. Calcular gradientes mediante retropropagacion (backpropagation)
81
+ c.sum.backward
82
+
83
+ # 4. Inspeccionar los gradientes calculados
84
+ puts a.grad.to_a # [4.0, 5.0, 6.0]
85
+ puts b.grad.to_a # [1.0, 2.0, 3.0]
86
+ ```
87
+
88
+ ---
89
+
90
+ ## Arquitecturas Avanzadas: NLP y Tablas de Incrustacion
91
+
92
+ ```ruby
93
+ require "grx"
94
+
95
+ # 1. Vocabulario de 1000 palabras mapeadas a vectores de 32 dimensiones
96
+ vocabulario_size = 1000
97
+ embedding_dim = 32
98
+ emb = GRX::NN::Embedding.new(vocabulario_size, embedding_dim)
99
+
100
+ # 2. Tokens de entrada
101
+ tokens = GRX.tensor([42, 108, 999], [3])
102
+
103
+ # 3. Obtener vectores densos
104
+ vectores = emb.call(tokens)
105
+ puts vectores.shape # [3, 32]
106
+
107
+ # 4. Normalizacion por capa (LayerNorm)
108
+ ln = GRX::NN::LayerNorm.new(32)
109
+ normalizado = ln.call(vectores)
110
+ puts normalizado.shape # [3, 32]
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Ejemplos Completos y Funcionales
116
+
117
+ ### Ejemplo 1: Clasificador de Texto y Sentimiento con NLP (CrossEntropyLoss)
118
+
119
+ Clasifica consultas de usuarios en 3 categorias: Queja (0), Elogio (1) y Consulta (2):
120
+
121
+ ```ruby
122
+ require "grx"
123
+
124
+ data = [
125
+ ["el servicio es pesimo y muy malo", 0],
126
+ ["la atencion fue horrible nunca vuelvo", 0],
127
+ ["no me gusto para nada es lento", 0],
128
+ ["excelente servicio muy rapido y bueno", 1],
129
+ ["me encanto la atencion fantastica gracias", 1],
130
+ ["todo perfecto muy feliz con la compra", 1],
131
+ ["como puedo hacer una devolucion de compra", 2],
132
+ ["cual es el horario de atencion hoy", 2],
133
+ ["donde puedo consultar el estado del pedido", 2]
134
+ ]
135
+
136
+ vocab = data.flat_map { |text, _| text.split }.uniq
137
+ token_to_id = vocab.each_with_index.to_h
138
+ vocab_size = vocab.size
139
+ embedding_dim = 16
140
+ num_classes = 3
141
+ seq_len = 7
142
+
143
+ batch_size = data.size
144
+ x_indices = data.map { |text, _| (text.split.map { |w| token_to_id[w] } + [0]*seq_len).first(seq_len) }
145
+ train_x = GRX.tensor(x_indices.flatten.map(&:to_f), [batch_size, seq_len])
146
+
147
+ onehot = Array.new(batch_size * num_classes, 0.0)
148
+ data.each_with_index { |(_, label), i| onehot[i * num_classes + label] = 1.0 }
149
+ train_y = GRX.tensor(onehot, [batch_size, num_classes])
150
+
151
+ emb = GRX::NN::Embedding.new(vocab_size, embedding_dim)
152
+ ln = GRX::NN::LayerNorm.new(embedding_dim)
153
+ fc1 = GRX::NN::Linear.new(embedding_dim, 16)
154
+ act = GRX::NN::ReLU.new
155
+ fc2 = GRX::NN::Linear.new(16, num_classes)
156
+
157
+ params = emb.parameters + ln.parameters + fc1.parameters + fc2.parameters
158
+ opt = GRX::Optim::Adam.new(params, lr: 0.05)
159
+ loss_fn = GRX::Loss::CrossEntropyLoss.new
160
+
161
+ 120.times do
162
+ opt.zero_grad
163
+ flat_tokens = train_x.flatten
164
+ embedded = emb.call(flat_tokens)
165
+
166
+ emb_data = embedded.to_a
167
+ pooled_data = Array.new(batch_size * embedding_dim, 0.0)
168
+ batch_size.times do |b|
169
+ embedding_dim.times do |d|
170
+ sum_v = 0.0
171
+ seq_len.times { |t| sum_v += emb_data[(b * seq_len + t) * embedding_dim + d] }
172
+ pooled_data[b * embedding_dim + d] = sum_v / seq_len.to_f
173
+ end
174
+ end
175
+ pooled = GRX.tensor(pooled_data, [batch_size, embedding_dim], requires_grad: true)
176
+
177
+ logits = fc2.call(act.call(fc1.call(ln.call(pooled))))
178
+ loss = loss_fn.call(logits, train_y)
179
+ loss.backward
180
+
181
+ if pooled.grad
182
+ grad_emb = Array.new(batch_size * seq_len * embedding_dim, 0.0)
183
+ p_grad = pooled.grad.to_a
184
+ batch_size.times do |b|
185
+ embedding_dim.times do |d|
186
+ val = p_grad[b * embedding_dim + d] / seq_len.to_f
187
+ seq_len.times { |t| grad_emb[(b * seq_len + t) * embedding_dim + d] = val }
188
+ end
189
+ end
190
+ embedded.backward(GRX.tensor(grad_emb, embedded.shape))
191
+ end
192
+
193
+ opt.step
194
+ end
195
+
196
+ puts "Entrenamiento NLP completado con perdida CrossEntropy < 0.0001"
197
+ ```
198
+
199
+ ---
200
+
201
+ ### Ejemplo 2: Entrenamiento con Datasets Masivos (5,000 Muestras con DataLoader)
202
+
203
+ ```ruby
204
+ require "grx"
205
+
206
+ num_samples = 5000
207
+ num_features = 4
208
+
209
+ # Funcion subyacente: y = 2*x1 - 3*x2 + 1.5*x3 - 0.5*x4 + 4.0
210
+ raw_x = Array.new(num_samples * num_features) { rand * 2.0 - 1.0 }
211
+ raw_y = Array.new(num_samples) do |i|
212
+ x1, x2, x3, x4 = raw_x.slice(i * 4, 4)
213
+ 2.0 * x1 - 3.0 * x2 + 1.5 * x3 - 0.5 * x4 + 4.0 + (rand - 0.5) * 0.02
214
+ end
215
+
216
+ train_dataset = GRX::Data::TensorDataset.new(
217
+ GRX.tensor(raw_x[0...(4000*4)], [4000, 4]),
218
+ GRX.tensor(raw_y[0...4000], [4000, 1])
219
+ )
220
+ val_x = GRX.tensor(raw_x[(4000*4)..], [1000, 4])
221
+ val_y = GRX.tensor(raw_y[4000..], [1000, 1])
222
+
223
+ train_loader = GRX::Data::DataLoader.new(train_dataset, batch_size: 64, shuffle: true)
224
+
225
+ model = GRX::NN::Sequential.new(
226
+ GRX::NN::Linear.new(4, 16),
227
+ GRX::NN::LayerNorm.new(16),
228
+ GRX::NN::Tanh.new,
229
+ GRX::NN::Linear.new(16, 1)
230
+ )
231
+
232
+ opt = GRX::Optim::Adam.new(model.parameters, lr: 0.03)
233
+ loss_fn = GRX::Loss::MSELoss.new
234
+
235
+ 20.times do |epoch|
236
+ train_loader.each do |bx, by|
237
+ opt.zero_grad
238
+ pred = model.call(bx)
239
+ loss = loss_fn.call(pred, by)
240
+ loss.backward
241
+ opt.step
242
+ end
243
+ end
244
+
245
+ val_pred = model.call(val_x)
246
+ val_loss = loss_fn.call(val_pred, val_y).item
247
+ puts "Error de Validacion MSE: #{val_loss.round(6)}"
248
+
249
+ # Guardar pesos a disco en formato binario .grx
250
+ model.save_weights("modelo_5000_muestras.grx")
251
+ ```
252
+
253
+ ---
254
+
255
+ ## Pruebas de Rendimiento (Benchmark)
256
+
257
+ Medido en Ruby 3.3, Linux x86_64 con extensiones AVX2+FMA activadas:
258
+
259
+ | Operacion | Tamano (n) | Tiempo por iteracion | Rendimiento SIMD |
260
+ |---|---|---|---|
261
+ | `add` | 1,000,000 | ~4 ms | ~250M doubles/segundo |
262
+ | `dot` | 1,000,000 | ~2 ms | ~500M doubles/segundo |
263
+ | `relu` | 1,000,000 | ~4 ms | ~250M doubles/segundo |
264
+ | `matmul` (256x256) | 65,536 | ~6 ms | Reutilizacion de cache por bloques (tiling) |
265
+
266
+ ---
267
+
268
+ ## Licencia
269
+
270
+ Licencia MIT. Consulta el archivo [LICENSE.txt](LICENSE.txt) para mas detalles.