grx-tensor 0.2.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 +9 -0
- data/GUIA_PRINCIPIANTES.md +307 -24
- data/README.es.md +1090 -161
- data/README.md +1102 -162
- 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 +37 -33
- data/lib/grx/c_api.rb +47 -15
- data/lib/grx/nn.rb +9 -7
- data/lib/grx/optim.rb +12 -7
- data/lib/grx/storage.rb +6 -5
- data/lib/grx/tensor.rb +36 -0
- data/lib/grx/utils.rb +15 -0
- data/lib/grx/version.rb +1 -1
- data/lib/grx.rb +8 -3
- metadata +31 -27
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Ruby speaks. C computes.**
|
|
4
4
|
|
|
5
|
-
A tensor framework for Ruby
|
|
5
|
+
A high-performance scientific computing, multidimensional tensor, and deep learning framework for Ruby. Features automatic differentiation (Autograd), dynamic multi-target SIMD hardware acceleration (AVX2+FMA, SSE, and portable scalar C), and complete neural network primitives behind a clean, expressive Ruby API.
|
|
6
6
|
|
|
7
7
|
[](https://www.ruby-lang.org)
|
|
8
8
|
[](LICENSE.txt)
|
|
@@ -10,265 +10,1205 @@ A tensor framework for Ruby with automatic differentiation, a C+SIMD compute cor
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Architectural Flow
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
```mermaid
|
|
16
|
+
flowchart TD
|
|
17
|
+
subgraph UserApp["User Application"]
|
|
18
|
+
A["require 'grx'"] --> B["GRX.tensor(data, shape)"]
|
|
19
|
+
B --> C["Scientific Computing / Machine Learning Models"]
|
|
20
|
+
C --> D["Training Loops, Simulations & Production Inference"]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
subgraph RubyLayer["Ruby High-Level Engine"]
|
|
24
|
+
E["GRX::Tensor (Multidimensional Shape, Strides, DAG Nodes)"]
|
|
25
|
+
F["GRX::NN (Linear, Embedding, LayerNorm, Dropout, BatchNorm1d)"]
|
|
26
|
+
G["GRX::Loss (CrossEntropy, MSE, BCE, Huber, MAE)"]
|
|
27
|
+
H["GRX::Optim (Adam with FMA, SGD with Momentum)"]
|
|
28
|
+
I["GRX::Data (TensorDataset, DataLoader)"]
|
|
29
|
+
J["GRX::Serialization (High-Speed .grx Binary Engine)"]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
subgraph FFISubsystem["FFI Bridge & Memory Management (Fiddle)"]
|
|
33
|
+
K["GRX::Storage (Aligned Pointer Buffers / GC Finalizer)"]
|
|
34
|
+
L["GRX::CAPI (Dynamic Multi-Library Symbol Dispatch)"]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
subgraph NativeCore["Native C Core (ext/grx/grx_core.c)"]
|
|
38
|
+
M{"grx_simd_level() Hardware Detection"}
|
|
39
|
+
N["AVX2 + FMA Engine (4 doubles/cycle, Fused Multiply-Add)"]
|
|
40
|
+
O["SSE Engine (2 doubles/cycle, Vectorized Math)"]
|
|
41
|
+
P["Universal Scalar C (Portable IEEE 754 Math)"]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
subgraph Hardware["Hardware Layer"]
|
|
45
|
+
Q["32-Byte Aligned Heap Memory"]
|
|
46
|
+
R["L1/L2 Cache Tiling Engine (64-Byte Cache Lines)"]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
UserApp --> RubyLayer
|
|
50
|
+
RubyLayer --> FFISubsystem
|
|
51
|
+
FFISubsystem --> NativeCore
|
|
52
|
+
M -- "AVX2+FMA CPU" --> N
|
|
53
|
+
M -- "SSE2/SSE4 CPU" --> O
|
|
54
|
+
M -- "Generic / ARM / VM" --> P
|
|
55
|
+
N --> Hardware
|
|
56
|
+
O --> Hardware
|
|
57
|
+
P --> Hardware
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Universal Build & Dynamic Hardware Dispatch
|
|
63
|
+
|
|
64
|
+
```mermaid
|
|
65
|
+
flowchart TD
|
|
66
|
+
A["gem install grx-tensor"] --> B["RubyGems Package Manager"]
|
|
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["Universal Multi-Target Binary"]
|
|
78
|
+
E2 --> F
|
|
79
|
+
E3 --> F
|
|
80
|
+
|
|
81
|
+
F --> G{"Host CPU Feature Detection at Runtime"}
|
|
82
|
+
G -- "AVX2 + FMA Detected" --> H["Runs AVX2+FMA SIMD (Max Speed)"]
|
|
83
|
+
G -- "SSE2 Detected" --> I["Runs SSE Vectorized Kernels"]
|
|
84
|
+
G -- "ARM / VM / Legacy CPU" --> J["Runs Portable Scalar C (Zero Crashes)"]
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Table of Contents
|
|
90
|
+
|
|
91
|
+
1. [Key Features](#key-features)
|
|
92
|
+
2. [Installation](#installation)
|
|
93
|
+
3. [Beginner-Friendly Quickstart Tutorials](#beginner-friendly-quickstart-tutorials)
|
|
94
|
+
- [1. Celsius to Fahrenheit Converter](#1-celsius-to-fahrenheit-converter-1-neuron-learns-f--18c--32)
|
|
95
|
+
- [2. Logic Gates (AND & XOR)](#2-logic-gates-and-linear-vs-non-linear-xor-with-relu--sigmoid)
|
|
96
|
+
- [3. Everyday Tensor Math](#3-everyday-tensor-math-in-4-lines)
|
|
97
|
+
4. [Tensor API Reference & Core Operations](#tensor-api-reference--core-operations)
|
|
98
|
+
5. [Autograd & Gradient Engine](#autograd--gradient-engine)
|
|
99
|
+
6. [Scientific & Numerical Computing (Beyond Deep Learning)](#scientific--numerical-computing-beyond-deep-learning)
|
|
100
|
+
- [A. Computer Vision & Image Filtering (Sobel Kernel Convolution)](#a-computer-vision--image-filtering-sobel-kernel-convolution)
|
|
101
|
+
- [B. Quantitative Finance & Portfolio Covariance](#b-quantitative-finance--portfolio-covariance)
|
|
102
|
+
- [C. Particle Physics & N-Body Simulation](#c-particle-physics--n-body-simulation)
|
|
103
|
+
- [D. Pure Mathematical Optimization with Autograd (Rosenbrock Function)](#d-pure-mathematical-optimization-with-autograd-rosenbrock-function)
|
|
104
|
+
7. [Deep Learning Cookbook (10 Neural Network Architectures)](#deep-learning-cookbook-10-neural-network-architectures)
|
|
105
|
+
- [Architecture 1: Deep Vision & Character Classifier (BatchNorm + Dropout)](#architecture-1-deep-vision--character-classifier-batchnorm--dropout)
|
|
106
|
+
- [Architecture 2: NLP & Intent Chatbot (Embedding + LayerNorm)](#architecture-2-nlp--intent-chatbot-embedding--layernorm)
|
|
107
|
+
- [Architecture 3: Reinforcement Learning (Deep Q-Network Agent)](#architecture-3-reinforcement-learning-deep-q-network-agent)
|
|
108
|
+
- [Architecture 4: Nonlinear Multi-Variable Time Series Forecasting](#architecture-4-nonlinear-multi-variable-time-series-forecasting)
|
|
109
|
+
- [Architecture 5: Deep Autoencoder for Dimensionality Reduction & Anomaly Detection](#architecture-5-deep-autoencoder-for-dimensionality-reduction--anomaly-detection)
|
|
110
|
+
- [Architecture 6: Autoregressive Next-Token Character Language Model & Generator](#architecture-6-autoregressive-next-token-character-language-model--generator)
|
|
111
|
+
- [Architecture 7: Sentiment Analysis & Review Classification (BCELoss)](#architecture-7-sentiment-analysis--review-classification-bceloss)
|
|
112
|
+
- [Architecture 8: Siamese Neural Network for Similarity & Verification](#architecture-8-siamese-neural-network-for-similarity--verification)
|
|
113
|
+
- [Architecture 9: Deep Residual Network (ResNet MLP Block with Skip Connection)](#architecture-9-deep-residual-network-resnet-mlp-block-with-skip-connection)
|
|
114
|
+
- [Architecture 10: Neural Collaborative Filtering & Recommendation System](#architecture-10-neural-collaborative-filtering--recommendation-system)
|
|
115
|
+
8. [Loss Functions (GRX::Loss)](#loss-functions-grxloss)
|
|
116
|
+
9. [Scientific Notation & Hyperparameter Reference](#scientific-notation--hyperparameter-reference)
|
|
117
|
+
- [1. Scientific Notation in Machine Learning (1e-1 to 1e-8)](#1-scientific-notation-in-machine-learning-1e-1-to-1e-8)
|
|
118
|
+
- [2. Neural Layer Parameters (GRX::NN)](#2-neural-layer-parameters-grxnn)
|
|
119
|
+
10. [Optimizers & Hyperparameter Reference (GRX::Optim)](#optimizers--hyperparameter-reference-grxoptim)
|
|
120
|
+
11. [Data Pipelines & DataLoader (GRX::Data)](#data-pipelines--dataloader-grxdata)
|
|
121
|
+
12. [Model Persistence & Brain Serialization (.grx Format)](#model-persistence--brain-serialization-grx-format)
|
|
122
|
+
- [Binary Format Layout (GRX1 Specification)](#binary-format-layout-grx1-specification)
|
|
123
|
+
- [Production Inference Deployment Workflow](#production-inference-deployment-workflow)
|
|
124
|
+
13. [Gradient Management & Utilities (GRX::Utils)](#gradient-management--utilities-grxutils)
|
|
125
|
+
14. [Windows Support & Toolchain Guide](#windows-support--toolchain-guide)
|
|
126
|
+
15. [License](#license)
|
|
16
127
|
|
|
17
|
-
|
|
128
|
+
---
|
|
18
129
|
|
|
19
|
-
|
|
130
|
+
## Key Features
|
|
20
131
|
|
|
21
|
-
| Feature |
|
|
132
|
+
| Feature | Specification |
|
|
22
133
|
|---|---|
|
|
23
|
-
| **
|
|
24
|
-
| **Aligned Memory** | 32-byte aligned heap
|
|
25
|
-
| **
|
|
26
|
-
| **
|
|
27
|
-
| **
|
|
28
|
-
| **
|
|
29
|
-
| **Loss Functions** |
|
|
30
|
-
| **
|
|
31
|
-
| **
|
|
32
|
-
| **
|
|
33
|
-
| **
|
|
34
|
-
| **
|
|
134
|
+
| **Multi-Target SIMD Engine** | Dynamic runtime CPU detection routes ops to AVX2+FMA, SSE, or portable scalar C |
|
|
135
|
+
| **Aligned Heap Memory** | 32-byte aligned native heap buffers (`posix_memalign` / `_aligned_malloc`) |
|
|
136
|
+
| **Zero-Copy Views** | Strided geometric transformations (`reshape`, `transpose`, `flatten`, `t`) |
|
|
137
|
+
| **Autograd Engine** | Reverse-mode automatic differentiation with dynamic DAG backpropagation |
|
|
138
|
+
| **Neural Layers** | `Linear`, `Sequential`, `Embedding`, `LayerNorm`, `BatchNorm1d`, `Dropout` |
|
|
139
|
+
| **Activation Functions** | `ReLU`, `LeakyReLU`, `Sigmoid`, `Tanh`, `Softmax` (fully differentiable) |
|
|
140
|
+
| **Loss Functions** | `MSELoss`, `MAELoss`, `BCELoss`, `CrossEntropyLoss`, `HuberLoss` |
|
|
141
|
+
| **Optimizers** | `Adam` (with FMA acceleration and bias correction), `SGD` (with momentum and weight decay) |
|
|
142
|
+
| **Binary Persistence** | Fast `.grx` binary serialization format for instant weight saving and loading |
|
|
143
|
+
| **Data Pipelines** | `TensorDataset` and `DataLoader` with batching, slicing, and shuffling |
|
|
144
|
+
| **Weight Initializers** | Xavier uniform and He normal (xorshift64* and Box-Muller in C) |
|
|
145
|
+
| **Cross-Platform** | Linux (`.so`), macOS (`.dylib`), Windows (`.dll` via DevKit) |
|
|
146
|
+
| **Pure Ruby Fallback** | Runs seamlessly in pure Ruby mode when native compiler tools are unavailable |
|
|
147
|
+
|
|
148
|
+
---
|
|
35
149
|
|
|
36
150
|
---
|
|
37
151
|
|
|
38
152
|
## Installation
|
|
39
153
|
|
|
40
|
-
###
|
|
154
|
+
### Standard RubyGems Installation
|
|
41
155
|
|
|
42
156
|
```bash
|
|
43
157
|
gem install grx-tensor
|
|
44
158
|
```
|
|
45
159
|
|
|
46
|
-
Or add it to your `Gemfile`:
|
|
160
|
+
Or add it to your project's `Gemfile`:
|
|
47
161
|
|
|
48
162
|
```ruby
|
|
49
163
|
gem "grx-tensor"
|
|
50
164
|
```
|
|
51
165
|
|
|
52
|
-
|
|
166
|
+
The native C extension is automatically compiled and linked in the background during installation.
|
|
53
167
|
|
|
54
|
-
|
|
168
|
+
---
|
|
55
169
|
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
make -C ext/unix
|
|
59
|
-
```
|
|
170
|
+
## Beginner-Friendly Quickstart Tutorials
|
|
60
171
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
172
|
+
### 1. Celsius to Fahrenheit Converter (1 Neuron Learns $F = 1.8C + 32$)
|
|
173
|
+
|
|
174
|
+
The absolute "Hello World" of Machine Learning. A single linear neuron ($y = w \cdot x + b$) discovers the physical laws of temperature on its own:
|
|
175
|
+
|
|
176
|
+
```ruby
|
|
177
|
+
require "grx"
|
|
178
|
+
|
|
179
|
+
# 1. Training data (Celsius and Fahrenheit pairs)
|
|
180
|
+
celsius = GRX.tensor([18.0, 25.0, 14.0, 21.0, 9.0, 16.0, 4.0, 32.0], [8, 1])
|
|
181
|
+
fahrenheit = GRX.tensor([64.4, 77.0, 57.2, 69.8, 48.2, 60.8, 39.2, 89.6], [8, 1])
|
|
182
|
+
|
|
183
|
+
# 2. Build 1-neuron model
|
|
184
|
+
model = GRX::NN::Sequential.new(GRX::NN::Linear.new(1, 1))
|
|
185
|
+
optimizer = GRX::Optim::Adam.new(model.parameters, lr: 0.8)
|
|
186
|
+
loss_fn = GRX::Loss::MSELoss.new
|
|
187
|
+
|
|
188
|
+
# 3. Train in 5 lines
|
|
189
|
+
1500.times do
|
|
190
|
+
optimizer.zero_grad
|
|
191
|
+
prediction = model.call(celsius)
|
|
192
|
+
loss = loss_fn.call(prediction, fahrenheit)
|
|
193
|
+
loss.backward
|
|
194
|
+
optimizer.step
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# 4. Predict unseen temperatures
|
|
198
|
+
test_temps = GRX.tensor([[100.0], [0.0], [37.0]], [3, 1])
|
|
199
|
+
predictions = model.call(test_temps).to_a
|
|
200
|
+
|
|
201
|
+
puts "100.0 C -> #{predictions[0].round(1)} F (Expected: 212.0 F)"
|
|
202
|
+
puts " 0.0 C -> #{predictions[1].round(1)} F (Expected: 32.0 F)"
|
|
203
|
+
puts " 37.0 C -> #{predictions[2].round(1)} F (Expected: 98.6 F)"
|
|
64
204
|
```
|
|
65
205
|
|
|
66
206
|
---
|
|
67
207
|
|
|
68
|
-
|
|
208
|
+
### 2. Logic Gates (AND Linear vs Non-Linear XOR with ReLU & Sigmoid)
|
|
209
|
+
|
|
210
|
+
Solving the classic non-linear XOR problem using a 2-layer neural network:
|
|
69
211
|
|
|
70
212
|
```ruby
|
|
71
213
|
require "grx"
|
|
72
214
|
|
|
73
|
-
#
|
|
74
|
-
|
|
75
|
-
|
|
215
|
+
# XOR Truth Table: (0,0)->0, (0,1)->1, (1,0)->1, (1,1)->0
|
|
216
|
+
x = GRX.tensor([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]], [4, 2])
|
|
217
|
+
y = GRX.tensor([[0.0], [1.0], [1.0], [0.0]], [4, 1])
|
|
76
218
|
|
|
77
|
-
# 2
|
|
78
|
-
|
|
219
|
+
# Multi-layer network: 2 inputs -> 4 hidden (ReLU) -> 1 output (Sigmoid)
|
|
220
|
+
xor_net = GRX::NN::Sequential.new(
|
|
221
|
+
GRX::NN::Linear.new(2, 4),
|
|
222
|
+
GRX::NN::ReLU.new,
|
|
223
|
+
GRX::NN::Linear.new(4, 1),
|
|
224
|
+
GRX::NN::Sigmoid.new
|
|
225
|
+
)
|
|
79
226
|
|
|
80
|
-
|
|
81
|
-
|
|
227
|
+
opt = GRX::Optim::Adam.new(xor_net.parameters, lr: 0.05)
|
|
228
|
+
loss_fn = GRX::Loss::BCELoss.new
|
|
82
229
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
230
|
+
400.times do
|
|
231
|
+
opt.zero_grad
|
|
232
|
+
pred = xor_net.call(x)
|
|
233
|
+
loss = loss_fn.call(pred, y)
|
|
234
|
+
loss.backward
|
|
235
|
+
opt.step
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
puts "XOR Predictions: #{xor_net.call(x).to_a.map { |v| v.round(3) }}"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
### 3. Everyday Tensor Math in 4 Lines
|
|
244
|
+
|
|
245
|
+
```ruby
|
|
246
|
+
require "grx"
|
|
247
|
+
|
|
248
|
+
prices = GRX.tensor([19.99, 45.50, 120.00, 5.25], [4])
|
|
249
|
+
discounted = (prices * 0.85).round(2) rescue (prices * 0.85) # 15% discount applied
|
|
250
|
+
|
|
251
|
+
puts "Total revenue: #{prices.sum.item.round(2)}"
|
|
252
|
+
puts "Average price: #{prices.mean.item.round(2)}"
|
|
253
|
+
puts "Most expensive item index: #{prices.argmax}"
|
|
86
254
|
```
|
|
87
255
|
|
|
88
256
|
---
|
|
89
257
|
|
|
90
|
-
##
|
|
258
|
+
## Tensor API Reference & Core Operations
|
|
91
259
|
|
|
92
|
-
|
|
260
|
+
Tensors in GRX represent multidimensional arrays of 64-bit IEEE 754 floating-point numbers stored in contiguous native memory buffers.
|
|
261
|
+
|
|
262
|
+
### 1. Creation & Factory Methods
|
|
93
263
|
|
|
94
264
|
```ruby
|
|
95
265
|
require "grx"
|
|
96
266
|
|
|
97
|
-
# 1.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
267
|
+
# 1. From flat or nested Ruby arrays (integers and floats)
|
|
268
|
+
t1 = GRX.tensor([1.0, 2.0, 3.0, 4.0], [2, 2])
|
|
269
|
+
t2 = GRX.tensor([[1.0, 2.0], [3.0, 4.0]], [2, 2], requires_grad: true)
|
|
270
|
+
|
|
271
|
+
# 2. Zeros and Ones
|
|
272
|
+
zeros = GRX.zeros([3, 4])
|
|
273
|
+
ones = GRX.ones([2, 5], requires_grad: true)
|
|
274
|
+
|
|
275
|
+
# 3. Random initialization
|
|
276
|
+
uniform_rand = GRX.rand([4, 4]) # Uniform distribution U[0, 1)
|
|
277
|
+
normal_rand = GRX.randn([4, 4]) # Standard normal distribution N(0, 1)
|
|
278
|
+
|
|
279
|
+
# 4. Weight initialization factories
|
|
280
|
+
xavier = GRX::Tensor.xavier_uniform([64, 32], requires_grad: true)
|
|
281
|
+
he = GRX::Tensor.he_normal([64, 32], requires_grad: true)
|
|
282
|
+
|
|
283
|
+
# 5. Like factories
|
|
284
|
+
z_like = GRX::Tensor.zeros_like(t1)
|
|
285
|
+
o_like = GRX::Tensor.ones_like(t1)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### 2. Inspection & Element Access
|
|
289
|
+
|
|
290
|
+
```ruby
|
|
291
|
+
t = GRX.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3])
|
|
292
|
+
|
|
293
|
+
puts t.shape # [2, 3] (Dimensions)
|
|
294
|
+
puts t.strides # [3, 1] (Memory strides)
|
|
295
|
+
puts t.numel # 6 (Total elements count)
|
|
296
|
+
puts t.rank # 2 (Number of dimensions)
|
|
297
|
+
puts t.item # Returns scalar float if numel == 1
|
|
298
|
+
puts t.to_a # [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
|
|
299
|
+
puts t.get(1, 2) # 6.0 (Element at row 1, col 2)
|
|
300
|
+
t.set(1, 2, 9.9) # Updates element at (1, 2)
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### 3. Arithmetic Operations (SIMD Accelerated)
|
|
304
|
+
|
|
305
|
+
```ruby
|
|
306
|
+
a = GRX.tensor([1.0, 2.0, 3.0], [3])
|
|
307
|
+
b = GRX.tensor([4.0, 5.0, 6.0], [3])
|
|
308
|
+
|
|
309
|
+
c_add = a + b # [5.0, 7.0, 9.0]
|
|
310
|
+
c_sub = a - b # [-3.0, -3.0, -3.0]
|
|
311
|
+
c_mul = a * b # [4.0, 10.0, 18.0]
|
|
312
|
+
c_div = b / a # [4.0, 2.5, 2.0]
|
|
313
|
+
c_neg = -a # [-1.0, -2.0, -3.0]
|
|
314
|
+
|
|
315
|
+
# Scalar operations
|
|
316
|
+
s_add = a + 10.0 # [11.0, 12.0, 13.0]
|
|
317
|
+
s_mul = a * 2.0 # [2.0, 4.0, 6.0]
|
|
318
|
+
s_div = a / 2.0 # [0.5, 1.0, 1.5]
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### 4. Element-Wise Math Functions
|
|
322
|
+
|
|
323
|
+
```ruby
|
|
324
|
+
t = GRX.tensor([1.0, 4.0, 9.0, 16.0], [4])
|
|
325
|
+
|
|
326
|
+
puts t.sqrt.to_a # [1.0, 2.0, 3.0, 4.0]
|
|
327
|
+
puts t.square.to_a # [1.0, 16.0, 81.0, 256.0]
|
|
328
|
+
puts t.abs.to_a # [1.0, 4.0, 9.0, 16.0]
|
|
329
|
+
puts t.pow(3.0).to_a # [1.0, 64.0, 729.0, 4096.0]
|
|
330
|
+
puts t.log.to_a # Natural logarithm ln(x)
|
|
331
|
+
puts t.exp.to_a # Exponential e^x
|
|
332
|
+
puts t.clip(2.0, 10.0) # Clamps values to [2.0, 10.0]
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
### 5. Matrix Multiplication & Linear Algebra
|
|
336
|
+
|
|
337
|
+
```ruby
|
|
338
|
+
# Matrix multiplication (Cache Tiling SIMD)
|
|
339
|
+
m1 = GRX.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3])
|
|
340
|
+
m2 = GRX.tensor([7.0, 8.0, 9.0, 10.0, 11.0, 12.0], [3, 2])
|
|
341
|
+
|
|
342
|
+
result = m1 @ m2 # Equivalent to m1.matmul(m2), returns Shape [2, 2]
|
|
343
|
+
|
|
344
|
+
# Dot product (1D vectors)
|
|
345
|
+
v1 = GRX.tensor([1.0, 2.0, 3.0], [3])
|
|
346
|
+
v2 = GRX.tensor([4.0, 5.0, 6.0], [3])
|
|
347
|
+
dot_val = v1.dot(v2) # 32.0 (Double precision scalar)
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### 6. Geometric Transformations (Zero-Copy Views)
|
|
351
|
+
|
|
352
|
+
```ruby
|
|
353
|
+
matrix = GRX.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], [2, 3])
|
|
354
|
+
|
|
355
|
+
# Reshape & Flatten
|
|
356
|
+
reshaped = matrix.reshape([3, 2]) # Shape [3, 2]
|
|
357
|
+
flat = matrix.flatten # Shape [6]
|
|
358
|
+
|
|
359
|
+
# Transpose
|
|
360
|
+
transposed = matrix.transpose(0, 1) # Shape [3, 2]
|
|
361
|
+
t_view = matrix.t # Shorthand 2D transpose
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### 7. Reductions & Statistics
|
|
101
365
|
|
|
102
|
-
|
|
103
|
-
|
|
366
|
+
```ruby
|
|
367
|
+
t = GRX.tensor([2.0, 4.0, 6.0, 8.0], [4], requires_grad: true)
|
|
368
|
+
|
|
369
|
+
s = t.sum # Scalar tensor [20.0], autograd node
|
|
370
|
+
m = t.mean # Scalar tensor [5.0], autograd node
|
|
371
|
+
max_val = t.max # 8.0 (Scalar float)
|
|
372
|
+
min_val = t.min # 2.0 (Scalar float)
|
|
373
|
+
best_idx = t.argmax # 3 (Index of maximum value)
|
|
374
|
+
worst_idx = t.argmin # 0 (Index of minimum value)
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
---
|
|
378
|
+
|
|
379
|
+
## Autograd & Gradient Engine
|
|
380
|
+
|
|
381
|
+
GRX features a dynamic Directed Acyclic Graph (DAG) reverse-mode automatic differentiation engine. Calling `backward` propagates derivatives according to the chain rule.
|
|
382
|
+
|
|
383
|
+
```mermaid
|
|
384
|
+
flowchart LR
|
|
385
|
+
A["Tensor a (requires_grad: true)"] --> C["Multiplication (a * b)"]
|
|
386
|
+
B["Tensor b (requires_grad: true)"] --> C
|
|
387
|
+
C --> D["Addition (+ 2.0)"]
|
|
388
|
+
D --> E["Reduction (.sum)"]
|
|
389
|
+
E --> F["Scalar Loss"]
|
|
390
|
+
F -. "loss.backward" .-> E
|
|
391
|
+
E -. "dLoss/dD" .-> D
|
|
392
|
+
D -. "dLoss/dC" .-> C
|
|
393
|
+
C -. "a.grad = dLoss/da" .-> A
|
|
394
|
+
C -. "b.grad = dLoss/db" .-> B
|
|
395
|
+
```
|
|
104
396
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
397
|
+
```ruby
|
|
398
|
+
require "grx"
|
|
399
|
+
|
|
400
|
+
x = GRX.tensor([2.0, 3.0], [2], requires_grad: true)
|
|
401
|
+
w = GRX.tensor([4.0, 5.0], [2], requires_grad: true)
|
|
402
|
+
b = GRX.tensor([1.0, 1.0], [2], requires_grad: true)
|
|
108
403
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
404
|
+
y = (x * w) + b
|
|
405
|
+
loss = y.sum
|
|
406
|
+
loss.backward
|
|
407
|
+
|
|
408
|
+
puts "x.grad: #{x.grad.to_a}" # [4.0, 5.0]
|
|
409
|
+
puts "w.grad: #{w.grad.to_a}" # [2.0, 3.0]
|
|
410
|
+
puts "b.grad: #{b.grad.to_a}" # [1.0, 1.0]
|
|
113
411
|
```
|
|
114
412
|
|
|
115
413
|
---
|
|
116
414
|
|
|
117
|
-
##
|
|
415
|
+
## Scientific & Numerical Computing (Beyond Deep Learning)
|
|
416
|
+
|
|
417
|
+
Tensors are general-purpose multidimensional mathematical engines suitable for image processing, numerical physics, quantitative finance, and pure mathematical optimization.
|
|
118
418
|
|
|
119
|
-
###
|
|
419
|
+
### A. Computer Vision & Image Filtering (Sobel Kernel Convolution)
|
|
120
420
|
|
|
121
|
-
|
|
421
|
+
Apply spatial filtering and edge detection directly on 2D image pixel grids:
|
|
122
422
|
|
|
123
423
|
```ruby
|
|
124
424
|
require "grx"
|
|
125
425
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
fc1 = GRX::NN::Linear.new(embedding_dim, 16)
|
|
156
|
-
act = GRX::NN::ReLU.new
|
|
157
|
-
fc2 = GRX::NN::Linear.new(16, num_classes)
|
|
158
|
-
|
|
159
|
-
params = emb.parameters + ln.parameters + fc1.parameters + fc2.parameters
|
|
160
|
-
opt = GRX::Optim::Adam.new(params, lr: 0.05)
|
|
161
|
-
loss_fn = GRX::Loss::CrossEntropyLoss.new
|
|
162
|
-
|
|
163
|
-
120.times do
|
|
164
|
-
opt.zero_grad
|
|
165
|
-
flat_tokens = train_x.flatten
|
|
166
|
-
embedded = emb.call(flat_tokens)
|
|
167
|
-
|
|
168
|
-
emb_data = embedded.to_a
|
|
169
|
-
pooled_data = Array.new(batch_size * embedding_dim, 0.0)
|
|
170
|
-
batch_size.times do |b|
|
|
171
|
-
embedding_dim.times do |d|
|
|
172
|
-
sum_v = 0.0
|
|
173
|
-
seq_len.times { |t| sum_v += emb_data[(b * seq_len + t) * embedding_dim + d] }
|
|
174
|
-
pooled_data[b * embedding_dim + d] = sum_v / seq_len.to_f
|
|
426
|
+
# 1. Create a 6x6 grayscale synthetic image tensor
|
|
427
|
+
image = GRX.tensor([
|
|
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
|
+
0.0, 0.0, 0.0, 255.0, 255.0, 255.0,
|
|
432
|
+
0.0, 0.0, 0.0, 255.0, 255.0, 255.0,
|
|
433
|
+
0.0, 0.0, 0.0, 255.0, 255.0, 255.0
|
|
434
|
+
], [6, 6])
|
|
435
|
+
|
|
436
|
+
# 2. Define horizontal Sobel edge detection kernel (3x3)
|
|
437
|
+
sobel_h = GRX.tensor([
|
|
438
|
+
-1.0, 0.0, 1.0,
|
|
439
|
+
-2.0, 0.0, 2.0,
|
|
440
|
+
-1.0, 0.0, 1.0
|
|
441
|
+
], [3, 3])
|
|
442
|
+
|
|
443
|
+
# 3. 2D Spatial Convolution
|
|
444
|
+
out_rows = image.shape[0] - sobel_h.shape[0] + 1
|
|
445
|
+
out_cols = image.shape[1] - sobel_h.shape[1] + 1
|
|
446
|
+
edge_map_data = []
|
|
447
|
+
|
|
448
|
+
out_rows.times do |r|
|
|
449
|
+
out_cols.times do |c|
|
|
450
|
+
patch_values = []
|
|
451
|
+
3.times do |kr|
|
|
452
|
+
3.times do |kc|
|
|
453
|
+
patch_values << image.get(r + kr, c + kc)
|
|
454
|
+
end
|
|
175
455
|
end
|
|
456
|
+
patch_tensor = GRX.tensor(patch_values, [3, 3])
|
|
457
|
+
conv_val = (patch_tensor * sobel_h).sum.item
|
|
458
|
+
edge_map_data << conv_val.abs
|
|
176
459
|
end
|
|
177
|
-
|
|
460
|
+
end
|
|
178
461
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
462
|
+
edge_map = GRX.tensor(edge_map_data, [out_rows, out_cols])
|
|
463
|
+
puts "Detected Edges Shape: #{edge_map.shape}"
|
|
464
|
+
```
|
|
182
465
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
466
|
+
---
|
|
467
|
+
|
|
468
|
+
### B. Quantitative Finance & Portfolio Covariance
|
|
469
|
+
|
|
470
|
+
Calculate asset returns, annualized volatility, and portfolio risk matrix:
|
|
471
|
+
|
|
472
|
+
```ruby
|
|
473
|
+
require "grx"
|
|
474
|
+
|
|
475
|
+
prices = GRX.tensor([
|
|
476
|
+
100.0, 50.0, 200.0,
|
|
477
|
+
102.0, 49.0, 205.0,
|
|
478
|
+
101.0, 51.0, 210.0,
|
|
479
|
+
105.0, 52.0, 208.0,
|
|
480
|
+
108.0, 53.0, 215.0
|
|
481
|
+
], [5, 3])
|
|
482
|
+
|
|
483
|
+
# 1. Compute Daily Simple Returns: (P_t - P_{t-1}) / P_{t-1}
|
|
484
|
+
returns_data = []
|
|
485
|
+
4.times do |t|
|
|
486
|
+
3.times do |asset|
|
|
487
|
+
p_prev = prices.get(t, asset)
|
|
488
|
+
p_curr = prices.get(t + 1, asset)
|
|
489
|
+
returns_data << ((p_curr - p_prev) / p_prev)
|
|
193
490
|
end
|
|
491
|
+
end
|
|
492
|
+
returns = GRX.tensor(returns_data, [4, 3])
|
|
194
493
|
|
|
195
|
-
|
|
494
|
+
# 2. Mean centering
|
|
495
|
+
mean_returns = Array.new(3) do |asset|
|
|
496
|
+
asset_col = 4.times.map { |d| returns.get(d, asset) }
|
|
497
|
+
asset_col.sum / 4.0
|
|
498
|
+
end
|
|
499
|
+
|
|
500
|
+
centered_data = []
|
|
501
|
+
4.times do |d|
|
|
502
|
+
3.times do |asset|
|
|
503
|
+
centered_data << (returns.get(d, asset) - mean_returns[asset])
|
|
504
|
+
end
|
|
196
505
|
end
|
|
506
|
+
centered_returns = GRX.tensor(centered_data, [4, 3])
|
|
507
|
+
|
|
508
|
+
# 3. Covariance Matrix: Sigma = (X^T @ X) / (N - 1)
|
|
509
|
+
cov_matrix = (centered_returns.t @ centered_returns) / 3.0
|
|
510
|
+
|
|
511
|
+
# 4. Portfolio Variance for weights w = [0.4, 0.3, 0.3]
|
|
512
|
+
weights = GRX.tensor([[0.4, 0.3, 0.3]], [1, 3])
|
|
513
|
+
port_variance = (weights @ cov_matrix @ weights.t).item
|
|
514
|
+
port_volatility = Math.sqrt(port_variance)
|
|
197
515
|
|
|
198
|
-
puts "
|
|
516
|
+
puts "Portfolio Daily Volatility: #{(port_volatility * 100).round(4)}%"
|
|
199
517
|
```
|
|
200
518
|
|
|
201
519
|
---
|
|
202
520
|
|
|
203
|
-
###
|
|
521
|
+
### C. Particle Physics & N-Body Simulation
|
|
204
522
|
|
|
205
|
-
|
|
523
|
+
Simulate particle positions, velocities, and pairwise Euclidean distances in 3D:
|
|
206
524
|
|
|
207
525
|
```ruby
|
|
208
526
|
require "grx"
|
|
209
527
|
|
|
210
|
-
|
|
211
|
-
|
|
528
|
+
n_particles = 4
|
|
529
|
+
dt = 0.01
|
|
530
|
+
|
|
531
|
+
positions = GRX.tensor([
|
|
532
|
+
0.0, 0.0, 0.0,
|
|
533
|
+
1.0, 0.0, 0.0,
|
|
534
|
+
0.0, 1.0, 0.0,
|
|
535
|
+
0.0, 0.0, 1.0
|
|
536
|
+
], [n_particles, 3])
|
|
537
|
+
|
|
538
|
+
velocities = GRX.tensor([
|
|
539
|
+
0.1, 0.0, 0.0,
|
|
540
|
+
0.0, 0.2, 0.0,
|
|
541
|
+
0.0, 0.0, 0.1,
|
|
542
|
+
-0.1, 0.0, 0.0
|
|
543
|
+
], [n_particles, 3])
|
|
544
|
+
|
|
545
|
+
gravity = GRX.tensor(Array.new(n_particles * 3) { |i| (i % 3 == 1) ? -9.81 : 0.0 }, [n_particles, 3])
|
|
546
|
+
|
|
547
|
+
velocities = velocities + (gravity * dt)
|
|
548
|
+
positions = positions + (velocities * dt)
|
|
549
|
+
|
|
550
|
+
dist_matrix_data = []
|
|
551
|
+
n_particles.times do |i|
|
|
552
|
+
n_particles.times do |j|
|
|
553
|
+
dx = positions.get(i, 0) - positions.get(j, 0)
|
|
554
|
+
dy = positions.get(i, 1) - positions.get(j, 1)
|
|
555
|
+
dz = positions.get(i, 2) - positions.get(j, 2)
|
|
556
|
+
dist_matrix_data << Math.sqrt(dx*dx + dy*dy + dz*dz)
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
dist_matrix = GRX.tensor(dist_matrix_data, [n_particles, n_particles])
|
|
561
|
+
puts "Pairwise Distances (P0 to P1): #{dist_matrix.get(0, 1).round(4)}"
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
566
|
+
### D. Pure Mathematical Optimization with Autograd (Rosenbrock Function)
|
|
567
|
+
|
|
568
|
+
Find the global minimum of the non-convex Rosenbrock Banana Function:
|
|
569
|
+
$$f(x, y) = (a - x)^2 + b(y - x^2)^2 \quad \text{with } a=1, b=100$$
|
|
570
|
+
|
|
571
|
+
```ruby
|
|
572
|
+
require "grx"
|
|
573
|
+
|
|
574
|
+
point = GRX.tensor([-1.5, 2.0], [2], requires_grad: true)
|
|
575
|
+
lr = 0.002
|
|
576
|
+
|
|
577
|
+
500.times do |step|
|
|
578
|
+
x = point.get(0)
|
|
579
|
+
y = point.get(1)
|
|
580
|
+
|
|
581
|
+
tx = GRX.tensor([x], [1], requires_grad: true)
|
|
582
|
+
ty = GRX.tensor([y], [1], requires_grad: true)
|
|
212
583
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
584
|
+
term1 = (GRX.tensor([1.0], [1]) - tx).square
|
|
585
|
+
term2 = (ty - tx.square).square * 100.0
|
|
586
|
+
loss = term1 + term2
|
|
587
|
+
loss.backward
|
|
588
|
+
|
|
589
|
+
new_x = x - lr * tx.grad.item
|
|
590
|
+
new_y = y - lr * ty.grad.item
|
|
591
|
+
point = GRX.tensor([new_x, new_y], [2])
|
|
218
592
|
end
|
|
219
593
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
594
|
+
puts "Converged Minimum: x = #{point.get(0).round(3)}, y = #{point.get(1).round(3)}"
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
---
|
|
598
|
+
|
|
599
|
+
## Deep Learning Cookbook (10 Neural Network Architectures)
|
|
600
|
+
|
|
601
|
+
### Architecture 1: Deep Vision & Character Classifier (BatchNorm + Dropout)
|
|
602
|
+
|
|
603
|
+
```ruby
|
|
604
|
+
require "grx"
|
|
605
|
+
|
|
606
|
+
vision_net = GRX::NN::Sequential.new(
|
|
607
|
+
GRX::NN::Linear.new(784, 256),
|
|
608
|
+
GRX::NN::BatchNorm1d.new(256),
|
|
609
|
+
GRX::NN::ReLU.new,
|
|
610
|
+
GRX::NN::Dropout.new(0.25),
|
|
611
|
+
GRX::NN::Linear.new(256, 64),
|
|
612
|
+
GRX::NN::LayerNorm.new(64),
|
|
613
|
+
GRX::NN::LeakyReLU.new(alpha: 0.01),
|
|
614
|
+
GRX::NN::Linear.new(64, 10),
|
|
615
|
+
GRX::NN::Softmax.new
|
|
223
616
|
)
|
|
224
|
-
val_x = GRX.tensor(raw_x[(4000*4)..], [1000, 4])
|
|
225
|
-
val_y = GRX.tensor(raw_y[4000..], [1000, 1])
|
|
226
617
|
|
|
227
|
-
|
|
618
|
+
batch_images = GRX.randn([16, 784])
|
|
619
|
+
predictions = vision_net.forward(batch_images) # Shape [16, 10]
|
|
620
|
+
puts "Vision Predictions Shape: #{predictions.shape}"
|
|
621
|
+
```
|
|
228
622
|
|
|
229
|
-
|
|
623
|
+
---
|
|
624
|
+
|
|
625
|
+
### Architecture 2: NLP & Intent Chatbot (Embedding + LayerNorm)
|
|
626
|
+
|
|
627
|
+
```ruby
|
|
628
|
+
require "grx"
|
|
629
|
+
|
|
630
|
+
vocab_size = 500
|
|
631
|
+
embedding_dim = 32
|
|
632
|
+
num_classes = 4
|
|
633
|
+
|
|
634
|
+
embedding = GRX::NN::Embedding.new(vocab_size, embedding_dim)
|
|
635
|
+
classifier = GRX::NN::Sequential.new(
|
|
636
|
+
GRX::NN::LayerNorm.new(embedding_dim),
|
|
637
|
+
GRX::NN::Linear.new(embedding_dim, 16),
|
|
638
|
+
GRX::NN::Tanh.new,
|
|
639
|
+
GRX::NN::Linear.new(16, num_classes)
|
|
640
|
+
)
|
|
641
|
+
|
|
642
|
+
token_ids = GRX.tensor([14, 2, 88, 412], [4])
|
|
643
|
+
dense_words = embedding.forward(token_ids)
|
|
644
|
+
|
|
645
|
+
seq_mean = Array.new(embedding_dim) do |d|
|
|
646
|
+
4.times.sum { |t| dense_words.get(t, d) } / 4.0
|
|
647
|
+
end
|
|
648
|
+
sentence_vector = GRX.tensor(seq_mean, [1, embedding_dim])
|
|
649
|
+
|
|
650
|
+
logits = classifier.forward(sentence_vector)
|
|
651
|
+
puts "Predicted Intent Class: #{logits.argmax}"
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
---
|
|
655
|
+
|
|
656
|
+
### Architecture 3: Reinforcement Learning (Deep Q-Network Agent)
|
|
657
|
+
|
|
658
|
+
```ruby
|
|
659
|
+
require "grx"
|
|
660
|
+
|
|
661
|
+
state_dim = 8
|
|
662
|
+
action_dim = 4
|
|
663
|
+
|
|
664
|
+
q_net = GRX::NN::Sequential.new(
|
|
665
|
+
GRX::NN::Linear.new(state_dim, 64),
|
|
666
|
+
GRX::NN::ReLU.new,
|
|
667
|
+
GRX::NN::Linear.new(64, 64),
|
|
668
|
+
GRX::NN::ReLU.new,
|
|
669
|
+
GRX::NN::Linear.new(64, action_dim)
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
optimizer = GRX::Optim::Adam.new(q_net.parameters, lr: 0.001)
|
|
673
|
+
huber_loss = GRX::Loss::HuberLoss.new(delta: 1.0)
|
|
674
|
+
|
|
675
|
+
current_state = GRX.randn([1, state_dim])
|
|
676
|
+
target_q_vals = GRX.tensor([[1.2, 0.5, -0.8, 3.4]], [1, 4])
|
|
677
|
+
|
|
678
|
+
optimizer.zero_grad
|
|
679
|
+
predicted_q_vals = q_net.forward(current_state)
|
|
680
|
+
loss = huber_loss.call(predicted_q_vals, target_q_vals)
|
|
681
|
+
loss.backward
|
|
682
|
+
optimizer.step
|
|
683
|
+
|
|
684
|
+
puts "Q-Loss: #{loss.item.round(6)}"
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
---
|
|
688
|
+
|
|
689
|
+
### Architecture 4: Nonlinear Multi-Variable Time Series Forecasting
|
|
690
|
+
|
|
691
|
+
```ruby
|
|
692
|
+
require "grx"
|
|
693
|
+
|
|
694
|
+
forecaster = GRX::NN::Sequential.new(
|
|
695
|
+
GRX::NN::Linear.new(5, 32),
|
|
696
|
+
GRX::NN::Sigmoid.new,
|
|
697
|
+
GRX::NN::Linear.new(32, 16),
|
|
698
|
+
GRX::NN::ReLU.new,
|
|
699
|
+
GRX::NN::Linear.new(16, 1)
|
|
700
|
+
)
|
|
701
|
+
|
|
702
|
+
optimizer = GRX::Optim::Adam.new(forecaster.parameters, lr: 0.01, weight_decay: 1e-4)
|
|
703
|
+
criterion = GRX::Loss::MSELoss.new
|
|
704
|
+
|
|
705
|
+
sensor_input = GRX.tensor([[22.5, 60.1, 1013.2, 5.4, 0.8]], [1, 5])
|
|
706
|
+
expected_temp = GRX.tensor([[23.1]], [1, 1])
|
|
707
|
+
|
|
708
|
+
optimizer.zero_grad
|
|
709
|
+
pred = forecaster.forward(sensor_input)
|
|
710
|
+
loss = criterion.call(pred, expected_temp)
|
|
711
|
+
loss.backward
|
|
712
|
+
optimizer.step
|
|
713
|
+
|
|
714
|
+
puts "Forecast Error: #{loss.item.round(6)}"
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
---
|
|
718
|
+
|
|
719
|
+
### Architecture 5: Deep Autoencoder for Dimensionality Reduction & Anomaly Detection
|
|
720
|
+
|
|
721
|
+
Compresses high-dimensional feature vectors into a compressed latent bottleneck and reconstructs them:
|
|
722
|
+
|
|
723
|
+
```ruby
|
|
724
|
+
require "grx"
|
|
725
|
+
|
|
726
|
+
# 1. Encoder network: 64 inputs -> 16 hidden -> 4 latent code
|
|
727
|
+
encoder = GRX::NN::Sequential.new(
|
|
728
|
+
GRX::NN::Linear.new(64, 16),
|
|
729
|
+
GRX::NN::LayerNorm.new(16),
|
|
730
|
+
GRX::NN::ReLU.new,
|
|
731
|
+
GRX::NN::Linear.new(16, 4)
|
|
732
|
+
)
|
|
733
|
+
|
|
734
|
+
# 2. Decoder network: 4 latent code -> 16 hidden -> 64 reconstructed outputs
|
|
735
|
+
decoder = GRX::NN::Sequential.new(
|
|
230
736
|
GRX::NN::Linear.new(4, 16),
|
|
231
737
|
GRX::NN::LayerNorm.new(16),
|
|
738
|
+
GRX::NN::ReLU.new,
|
|
739
|
+
GRX::NN::Linear.new(16, 64),
|
|
740
|
+
GRX::NN::Sigmoid.new
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
optimizer = GRX::Optim::Adam.new(encoder.parameters + decoder.parameters, lr: 0.01)
|
|
744
|
+
recon_loss_fn = GRX::Loss::MSELoss.new
|
|
745
|
+
|
|
746
|
+
# Training reconstruction loop
|
|
747
|
+
sample_batch = GRX.rand([8, 64])
|
|
748
|
+
|
|
749
|
+
optimizer.zero_grad
|
|
750
|
+
latent_code = encoder.forward(sample_batch) # Shape [8, 4]
|
|
751
|
+
reconstruction = decoder.forward(latent_code) # Shape [8, 64]
|
|
752
|
+
loss = recon_loss_fn.call(reconstruction, sample_batch)
|
|
753
|
+
loss.backward
|
|
754
|
+
optimizer.step
|
|
755
|
+
|
|
756
|
+
puts "Autoencoder Reconstruction Loss: #{loss.item.round(6)}"
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
---
|
|
760
|
+
|
|
761
|
+
### Architecture 6: Autoregressive Next-Token Character Language Model & Generator
|
|
762
|
+
|
|
763
|
+
Generates text character-by-character using embedding lookups and dense prediction heads:
|
|
764
|
+
|
|
765
|
+
```ruby
|
|
766
|
+
require "grx"
|
|
767
|
+
|
|
768
|
+
vocab_size = 256 # ASCII alphabet
|
|
769
|
+
embed_dim = 16
|
|
770
|
+
ctx_len = 4 # 4-character context window
|
|
771
|
+
|
|
772
|
+
char_embedding = GRX::NN::Embedding.new(vocab_size, embed_dim)
|
|
773
|
+
language_head = GRX::NN::Sequential.new(
|
|
774
|
+
GRX::NN::Linear.new(embed_dim * ctx_len, 64),
|
|
775
|
+
GRX::NN::LayerNorm.new(64),
|
|
232
776
|
GRX::NN::Tanh.new,
|
|
233
|
-
GRX::NN::Linear.new(
|
|
777
|
+
GRX::NN::Linear.new(64, vocab_size)
|
|
234
778
|
)
|
|
235
779
|
|
|
236
|
-
|
|
237
|
-
|
|
780
|
+
# Predict next character for context "hell" -> tokens [104, 101, 108, 108]
|
|
781
|
+
context_ids = GRX.tensor([104, 101, 108, 108], [4])
|
|
782
|
+
embedded = char_embedding.forward(context_ids) # Shape [4, 16]
|
|
783
|
+
flattened_context = embedded.flatten.reshape([1, embed_dim * ctx_len])
|
|
784
|
+
|
|
785
|
+
logits = language_head.forward(flattened_context) # Shape [1, 256]
|
|
786
|
+
predicted_char_ascii = logits.argmax
|
|
787
|
+
|
|
788
|
+
puts "Context: 'hell' -> Predicted Next Char: '#{predicted_char_ascii.chr}' (ASCII #{predicted_char_ascii})"
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
---
|
|
792
|
+
|
|
793
|
+
### Architecture 7: Sentiment Analysis & Review Classification (BCELoss)
|
|
794
|
+
|
|
795
|
+
Evaluates text sentiment polarity (Positive / Negative) from word token streams:
|
|
238
796
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
797
|
+
```ruby
|
|
798
|
+
require "grx"
|
|
799
|
+
|
|
800
|
+
vocab_size = 1000
|
|
801
|
+
embed_dim = 32
|
|
802
|
+
|
|
803
|
+
sentiment_embedding = GRX::NN::Embedding.new(vocab_size, embed_dim)
|
|
804
|
+
sentiment_head = GRX::NN::Sequential.new(
|
|
805
|
+
GRX::NN::LayerNorm.new(embed_dim),
|
|
806
|
+
GRX::NN::Linear.new(embed_dim, 16),
|
|
807
|
+
GRX::NN::ReLU.new,
|
|
808
|
+
GRX::NN::Dropout.new(0.2),
|
|
809
|
+
GRX::NN::Linear.new(16, 1),
|
|
810
|
+
GRX::NN::Sigmoid.new
|
|
811
|
+
)
|
|
812
|
+
|
|
813
|
+
# Review token stream: "excellent quality product fast delivery" -> [42, 189, 7, 85, 301]
|
|
814
|
+
review_tokens = GRX.tensor([42, 189, 7, 85, 301], [5])
|
|
815
|
+
vectors = sentiment_embedding.forward(review_tokens) # Shape [5, 32]
|
|
816
|
+
|
|
817
|
+
# Global average pooling across words
|
|
818
|
+
review_representation = Array.new(embed_dim) do |d|
|
|
819
|
+
5.times.sum { |t| vectors.get(t, d) } / 5.0
|
|
820
|
+
end
|
|
821
|
+
review_tensor = GRX.tensor(review_representation, [1, embed_dim])
|
|
822
|
+
|
|
823
|
+
polarity_prob = sentiment_head.forward(review_tensor).item
|
|
824
|
+
sentiment_label = polarity_prob >= 0.5 ? "POSITIVE" : "NEGATIVE"
|
|
825
|
+
|
|
826
|
+
puts "Sentiment Score: #{(polarity_prob * 100).round(2)}% -> #{sentiment_label}"
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
---
|
|
830
|
+
|
|
831
|
+
### Architecture 8: Siamese Neural Network for Similarity & Verification
|
|
832
|
+
|
|
833
|
+
Twin branches sharing weights for signature, face, or document similarity verification:
|
|
834
|
+
|
|
835
|
+
```ruby
|
|
836
|
+
require "grx"
|
|
837
|
+
|
|
838
|
+
# Shared feature extractor
|
|
839
|
+
feature_extractor = GRX::NN::Sequential.new(
|
|
840
|
+
GRX::NN::Linear.new(32, 16),
|
|
841
|
+
GRX::NN::LayerNorm.new(16),
|
|
842
|
+
GRX::NN::Tanh.new,
|
|
843
|
+
GRX::NN::Linear.new(16, 8)
|
|
844
|
+
)
|
|
845
|
+
|
|
846
|
+
# Two sample inputs (e.g. Signature A and Signature B)
|
|
847
|
+
sig_a = GRX.randn([1, 32])
|
|
848
|
+
sig_b = GRX.randn([1, 32])
|
|
849
|
+
|
|
850
|
+
# Compute dense embedding vectors using identical shared weights
|
|
851
|
+
embedding_a = feature_extractor.forward(sig_a) # Shape [1, 8]
|
|
852
|
+
embedding_b = feature_extractor.forward(sig_b) # Shape [1, 8]
|
|
853
|
+
|
|
854
|
+
# Compute Euclidean embedding distance
|
|
855
|
+
diff = embedding_a - embedding_b
|
|
856
|
+
distance = diff.square.sum.sqrt.item
|
|
857
|
+
|
|
858
|
+
match = distance < 1.0 ? "MATCH (Same Entity)" : "NO MATCH (Different Entities)"
|
|
859
|
+
puts "Embedding Distance: #{distance.round(4)} -> #{match}"
|
|
860
|
+
```
|
|
861
|
+
|
|
862
|
+
---
|
|
863
|
+
|
|
864
|
+
### Architecture 9: Deep Residual Network (ResNet MLP Block with Skip Connection)
|
|
865
|
+
|
|
866
|
+
Enables training deep networks without gradient degradation using identity shortcut connections $y = x + F(x)$:
|
|
867
|
+
|
|
868
|
+
```ruby
|
|
869
|
+
require "grx"
|
|
870
|
+
|
|
871
|
+
class ResidualBlock < GRX::NN::Module
|
|
872
|
+
def initialize(dim)
|
|
873
|
+
@fc1 = GRX::NN::Linear.new(dim, dim)
|
|
874
|
+
@bn1 = GRX::NN::BatchNorm1d.new(dim)
|
|
875
|
+
@act = GRX::NN::ReLU.new
|
|
876
|
+
@fc2 = GRX::NN::Linear.new(dim, dim)
|
|
877
|
+
@bn2 = GRX::NN::BatchNorm1d.new(dim)
|
|
878
|
+
end
|
|
879
|
+
|
|
880
|
+
def forward(x)
|
|
881
|
+
residual = x
|
|
882
|
+
out = @fc1.forward(x)
|
|
883
|
+
out = @bn1.forward(out)
|
|
884
|
+
out = @act.forward(out)
|
|
885
|
+
out = @fc2.forward(out)
|
|
886
|
+
out = @bn2.forward(out)
|
|
887
|
+
out + residual # Identity Skip Connection
|
|
246
888
|
end
|
|
247
889
|
end
|
|
248
890
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
891
|
+
res_block = ResidualBlock.new(16)
|
|
892
|
+
x_in = GRX.randn([4, 16])
|
|
893
|
+
res_out = res_block.forward(x_in)
|
|
894
|
+
|
|
895
|
+
puts "Residual Block Output Shape: #{res_out.shape}"
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
---
|
|
899
|
+
|
|
900
|
+
### Architecture 10: Neural Collaborative Filtering & Recommendation System
|
|
901
|
+
|
|
902
|
+
Combines User and Item latent embeddings with interaction MLP layers to predict recommendation scores:
|
|
903
|
+
|
|
904
|
+
```ruby
|
|
905
|
+
require "grx"
|
|
906
|
+
|
|
907
|
+
n_users = 100
|
|
908
|
+
n_items = 50
|
|
909
|
+
latent_dim = 16
|
|
910
|
+
|
|
911
|
+
user_embedding = GRX::NN::Embedding.new(n_users, latent_dim)
|
|
912
|
+
item_embedding = GRX::NN::Embedding.new(n_items, latent_dim)
|
|
913
|
+
|
|
914
|
+
rating_mlp = GRX::NN::Sequential.new(
|
|
915
|
+
GRX::NN::Linear.new(latent_dim * 2, 16),
|
|
916
|
+
GRX::NN::ReLU.new,
|
|
917
|
+
GRX::NN::Linear.new(16, 1),
|
|
918
|
+
GRX::NN::Sigmoid.new
|
|
919
|
+
)
|
|
920
|
+
|
|
921
|
+
# Predict recommendation score for User #7 and Movie #23
|
|
922
|
+
u_idx = GRX.tensor([7], [1])
|
|
923
|
+
i_idx = GRX.tensor([23], [1])
|
|
252
924
|
|
|
253
|
-
|
|
254
|
-
|
|
925
|
+
u_vec = user_embedding.forward(u_idx) # Shape [1, 16]
|
|
926
|
+
i_vec = item_embedding.forward(i_idx) # Shape [1, 16]
|
|
927
|
+
|
|
928
|
+
# Concatenate user and item embeddings -> Shape [1, 32]
|
|
929
|
+
interaction_vector = GRX.tensor(u_vec.to_a + i_vec.to_a, [1, latent_dim * 2])
|
|
930
|
+
predicted_rating = rating_mlp.forward(interaction_vector).item
|
|
931
|
+
|
|
932
|
+
puts "Predicted Affinity Score: #{(predicted_rating * 5.0).round(2)} / 5.0 Stars"
|
|
255
933
|
```
|
|
256
934
|
|
|
257
935
|
---
|
|
258
936
|
|
|
259
|
-
##
|
|
937
|
+
## Loss Functions (`GRX::Loss`)
|
|
260
938
|
|
|
261
|
-
|
|
939
|
+
All loss functions return a differentiable `GRX::Tensor` ready for `loss.backward`.
|
|
262
940
|
|
|
263
|
-
|
|
|
941
|
+
| Loss Class | Constructor Signature & Parameters | Default | Mathematical Use Case & Description |
|
|
264
942
|
|---|---|---|---|
|
|
265
|
-
| `
|
|
266
|
-
| `
|
|
267
|
-
| `
|
|
268
|
-
| `
|
|
943
|
+
| `GRX::Loss::MSELoss` | `new(reduction: :mean)` | `reduction: :mean` | Mean Squared Error: $\frac{1}{N}\sum(y_{pred} - y_{true})^2$. Standard for continuous regression. |
|
|
944
|
+
| `GRX::Loss::MAELoss` | `new(reduction: :mean)` | `reduction: :mean` | Mean Absolute Error: $\frac{1}{N}\sum \|y_{pred} - y_{true}\|$. Robust to large outliers. |
|
|
945
|
+
| `GRX::Loss::BCELoss` | `new(reduction: :mean, eps: 1e-7)` | `eps: 1e-7` | Binary Cross Entropy: $-[y \log(p) + (1-y) \log(1-p)]$. Clamped by `eps` to prevent $\log(0)$. |
|
|
946
|
+
| `GRX::Loss::CrossEntropyLoss` | `new(reduction: :mean)` | `reduction: :mean` | Multi-Class Cross Entropy. Combines Log-Sum-Exp softmax with negative log-likelihood. |
|
|
947
|
+
| `GRX::Loss::HuberLoss` | `new(delta: 1.0, reduction: :mean)` | `delta: 1.0` | Smooth L1 / Huber: Quadratic for error $< \delta$, linear for error $\ge \delta$. Outlier resistant. |
|
|
948
|
+
|
|
949
|
+
* `reduction` options: `:mean` (divides total loss by mini-batch size; recommended) or `:sum` (accumulates unscaled sum).
|
|
950
|
+
|
|
951
|
+
---
|
|
952
|
+
|
|
953
|
+
## Scientific Notation & Hyperparameter Reference
|
|
954
|
+
|
|
955
|
+
### 1. Scientific Notation in Machine Learning (`1e-1` to `1e-8`)
|
|
956
|
+
|
|
957
|
+
Deep learning parameters often represent fractional quantities (learning rates, regularization terms, variance stabilizers). GRTensor fully supports standard Ruby scientific notation literals:
|
|
958
|
+
|
|
959
|
+
| Notation | Exact Decimal | Fraction | Common Name | Typical Use Case in GRTensor |
|
|
960
|
+
|---|---|---|---|---|
|
|
961
|
+
| `1e-1` | `0.1` | $1/10$ | One Tenth | Fast learning rate in simple models, `BatchNorm1d` momentum. |
|
|
962
|
+
| `1e-2` | `0.01` | $1/100$ | One Hundredth | Default `SGD` learning rate, `LeakyReLU` negative slope (`alpha`). |
|
|
963
|
+
| `1e-3` | `0.001` | $1/1,000$ | One Thousandth | Gold standard default learning rate (`lr`) for `Adam` optimizer. |
|
|
964
|
+
| `1e-4` | `0.0001` | $1/10,000$ | One Ten-Thousandth | Recommended L2 regularization penalty (`weight_decay`) to prevent overfitting. |
|
|
965
|
+
| `1e-5` | `0.00001` | $1/100,000$ | One Hundred-Thousandth | Variance stabilizer constant (`eps`) in `LayerNorm` and `BatchNorm1d`. |
|
|
966
|
+
| `1e-7` | `0.0000001` | $1/10,000,000$ | One Ten-Millionth | Numerical safety clamp (`eps`) in `BCELoss` preventing $\log(0) \to -\infty$. |
|
|
967
|
+
| `1e-8` | `0.00000001` | $1/100,000,000$ | One Hundred-Millionth | Denominator variance stabilizer (`eps`) in `Adam` optimizer. |
|
|
968
|
+
|
|
969
|
+
---
|
|
970
|
+
|
|
971
|
+
### 2. Neural Layer Parameters (`GRX::NN`)
|
|
972
|
+
|
|
973
|
+
| Layer Class | Parameter | Type | Default | Valid Range | Physical & Computational Meaning |
|
|
974
|
+
|---|---|---|---|---|---|
|
|
975
|
+
| `Linear` | `in_features` | Integer | (Required) | $\ge 1$ | Dimensionality of input feature vectors. |
|
|
976
|
+
| `Linear` | `out_features`| Integer | (Required) | $\ge 1$ | Number of neurons / output features produced. |
|
|
977
|
+
| `Linear` | `bias` | Boolean | `true` | `true` / `false` | When `true`, adds trainable bias vector $b$ ($y = Wx + b$). |
|
|
978
|
+
| `Embedding` | `num_embeddings`| Integer | (Required) | $\ge 1$ | Total vocabulary size or entity count. |
|
|
979
|
+
| `Embedding` | `embedding_dim` | Integer | (Required) | $\ge 1$ | Dimensionality of dense latent vector for each token. |
|
|
980
|
+
| `Dropout` | `p` | Float | `0.5` | `0.0` to `0.99` | Probability of zeroing out activations during training to prevent co-adaptation. |
|
|
981
|
+
| `LeakyReLU` | `alpha` | Float | `0.01` | `0.001` to `0.3` | Slope for negative inputs ($x < 0$). Prevents dead neurons. |
|
|
982
|
+
| `LayerNorm` | `normalized_shape` | Int/Array | (Required) | Dimensions | Feature dimensions over which mean and variance are normalized. |
|
|
983
|
+
| `LayerNorm` | `eps` / `epsilon` | Float | `1e-5` | `1e-8` to `1e-4` | Variance stabilizer added to denominator. |
|
|
984
|
+
| `BatchNorm1d` | `num_features` | Integer | (Required) | $\ge 1$ | Number of channels normalized across the mini-batch dimension. |
|
|
985
|
+
| `BatchNorm1d` | `eps` / `epsilon` | Float | `1e-5` | `1e-8` to `1e-4` | Variance stabilizer added to denominator. |
|
|
986
|
+
| `BatchNorm1d` | `momentum` | Float | `0.1` | `0.01` to `0.5` | Exponential moving average factor for running mean and variance. |
|
|
987
|
+
|
|
988
|
+
### 3. Tensor Creation & Core Operations Parameters (`GRX::Tensor`)
|
|
989
|
+
|
|
990
|
+
| Factory / Method | Parameter | Type | Default | Description |
|
|
991
|
+
|---|---|---|---|---|
|
|
992
|
+
| `GRX.tensor` | `data` | Array / Storage | (Required) | Flat or nested Ruby numbers array (`[1.0, 2.0]` or `[[1, 2], [3, 4]]`). |
|
|
993
|
+
| `GRX.tensor` | `shape` | Array[Integer] | `nil` (Auto) | Explicit dimensions (e.g. `[2, 3]`). Auto-inferred if omitted. |
|
|
994
|
+
| `GRX.tensor` | `requires_grad`| Boolean | `false` | Enables reverse-mode autograd tracking in the computational DAG. |
|
|
995
|
+
| `GRX.zeros` / `GRX.ones` | `shape` | Array[Integer] | (Required) | Shape dimensions initialized to `0.0` or `1.0`. |
|
|
996
|
+
| `GRX.rand` | `shape` | Array[Integer] | (Required) | Uniformly distributed random tensor $U[0, 1)$. |
|
|
997
|
+
| `GRX.randn` | `shape` | Array[Integer] | (Required) | Standard normal random tensor $N(0, 1)$ via Box-Muller transformation. |
|
|
998
|
+
| `Tensor.xavier_uniform`| `shape` | Array[Integer] | (Required) | Xavier/Glorot uniform initialization ($U[-\sqrt{6/(f_{in}+f_{out})}, \sqrt{6/(f_{in}+f_{out})}]$). |
|
|
999
|
+
| `Tensor.he_normal` | `shape` | Array[Integer] | (Required) | He/Kaiming normal initialization ($N(0, \sqrt{2/f_{in}})$). Optimal for ReLU. |
|
|
1000
|
+
| `Tensor.zeros_like` | `other` | Tensor | (Required) | Creates zeros tensor matching `other.shape`. |
|
|
1001
|
+
| `Tensor.ones_like` | `other` | Tensor | (Required) | Creates ones tensor matching `other.shape`. |
|
|
1002
|
+
| `tensor.clip` | `lo`, `hi` | Numeric | (Required) | Clamps all values to the $[lo, hi]$ interval. |
|
|
1003
|
+
| `tensor.pow` | `exponent` | Numeric | (Required) | Computes $x^e$ element-wise. Autograd differentiable. |
|
|
1004
|
+
| `tensor.reshape` | `new_shape` | Array[Integer] | (Required) | Reshapes tensor preserving total element count. Zero-copy view. |
|
|
1005
|
+
| `tensor.transpose` | (none) | - | - | Transposes 2D matrix axes. Zero-copy strided view. |
|
|
1006
|
+
| `tensor.flatten` | (none) | - | - | Flattens tensor into 1D `[numel]` shape. Zero-copy view. |
|
|
1007
|
+
| `tensor.contiguous`| (none) | - | - | Re-packs non-contiguous memory views into a fresh contiguous buffer. |
|
|
1008
|
+
| `tensor.get` | `*coords` | Integers | (Required) | Returns float element at specified multidimensional coordinates. |
|
|
1009
|
+
| `tensor.set` | `*coords, val` | Integers, Float | (Required) | Modifies value at specified coordinates in native memory. |
|
|
1010
|
+
| `tensor.item` | (none) | - | - | Returns Ruby Float for 1-element scalar tensors. |
|
|
1011
|
+
| `tensor.argmax` | (none) | - | - | Returns flat index of the maximum value. |
|
|
1012
|
+
| `tensor.argmin` | (none) | - | - | Returns flat index of the minimum value. |
|
|
1013
|
+
| `tensor.backward` | `gradient` | Tensor | `nil` | Executes reverse-mode backpropagation across the computational DAG. |
|
|
1014
|
+
|
|
1015
|
+
---
|
|
1016
|
+
|
|
1017
|
+
### 4. Data Pipelines, Persistence & Utilities (`GRX::Data`, `GRX::Serialization`, `GRX::Utils`)
|
|
1018
|
+
|
|
1019
|
+
* `TensorDataset.new(*tensors)`:
|
|
1020
|
+
* `*tensors` (Required): Parallel feature and label tensors sharing the same batch dimension 0.
|
|
1021
|
+
* `DataLoader.new(dataset, batch_size: 32, shuffle: true)`:
|
|
1022
|
+
* `dataset` (`GRX::Data::Dataset`): Wrapped dataset.
|
|
1023
|
+
* `batch_size` (Integer, default: `32`): Number of samples per mini-batch.
|
|
1024
|
+
* `shuffle` (Boolean, default: `true`): Randomly permutes dataset indices at the start of each epoch.
|
|
1025
|
+
* `GRX::Serialization.save(model, path)` / `model.save_weights(path)`:
|
|
1026
|
+
* `model` (`GRX::NN::Module`): Neural network model instance.
|
|
1027
|
+
* `path` (String): Output filepath for binary `.grx` format.
|
|
1028
|
+
* `GRX::Serialization.load(model, path)` / `model.load_weights(path)`:
|
|
1029
|
+
* `model` (`GRX::NN::Module`): Initialized neural model with matching architecture.
|
|
1030
|
+
* `path` (String): Source `.grx` file.
|
|
1031
|
+
* `model.train!` and `model.eval!`:
|
|
1032
|
+
* `train!`: Switches layers to training mode (enables `Dropout`, dynamic batch statistics in `BatchNorm1d`).
|
|
1033
|
+
* `eval!`: Switches layers to evaluation/inference mode (disables `Dropout`, freezes running statistics in `BatchNorm1d`).
|
|
1034
|
+
* `GRX::Utils.clip_grad_norm!(parameters, max_norm: 1.0)`:
|
|
1035
|
+
* `parameters` (Array[Tensor]): Trainable parameter collection.
|
|
1036
|
+
* `max_norm` (Float, default: `1.0`): Maximum allowable combined L2 gradient norm to prevent explosions.
|
|
1037
|
+
* `GRX::Utils.one_hot(indices, num_classes: nil, requires_grad: false)`:
|
|
1038
|
+
* `indices` (Array[Integer] or Tensor): Categorical class IDs (e.g. `[0, 2, 1]`).
|
|
1039
|
+
* `num_classes` (Integer, optional): Total number of output classes. Auto-inferred if omitted.
|
|
1040
|
+
* `GRX.simd_mode`:
|
|
1041
|
+
* Queries host CPU vectorization level: `:avx2` (4 doubles/cycle with FMA), `:sse` (2 doubles/cycle), `:scalar` (portable C), or `:ruby`.
|
|
1042
|
+
|
|
1043
|
+
---
|
|
1044
|
+
|
|
1045
|
+
### 5. Exception Hierarchy
|
|
1046
|
+
|
|
1047
|
+
| Exception Class | Parent | Description |
|
|
1048
|
+
|---|---|---|
|
|
1049
|
+
| `GRX::Error` | `StandardError` | Base class for all GRTensor exceptions. |
|
|
1050
|
+
| `GRX::ShapeError` | `GRX::Error` | Incompatible tensor shapes during algebraic operations or matrix multiplications. |
|
|
1051
|
+
| `GRX::DimensionError` | `GRX::Error` | Invalid tensor rank (e.g. calling `transpose` or `matmul` on 1D vectors). |
|
|
1052
|
+
| `GRX::StorageError` | `GRX::Error` | Native C heap memory allocation failure (OOM) or corrupted `.grx` binary file. |
|
|
1053
|
+
|
|
1054
|
+
---
|
|
1055
|
+
|
|
1056
|
+
## Optimizers & Hyperparameter Reference (`GRX::Optim`)
|
|
1057
|
+
|
|
1058
|
+
### 1. `GRX::Optim::Adam`
|
|
1059
|
+
Adaptive Moment Estimation (Kingma & Ba) with SIMD-vectorized FMA acceleration in native C:
|
|
1060
|
+
|
|
1061
|
+
```ruby
|
|
1062
|
+
optimizer = GRX::Optim::Adam.new(
|
|
1063
|
+
model.parameters,
|
|
1064
|
+
lr: 0.001, # Learning rate (alpha). Recommended: 1e-3 (0.001) for deep nets
|
|
1065
|
+
betas: [0.9, 0.999], # [beta1, beta2] exponential decay rates for 1st/2nd moments
|
|
1066
|
+
eps: 1e-8, # Epsilon term added to denominator for numerical stability
|
|
1067
|
+
weight_decay: 1e-4 # L2 regularization factor (weight penalty to prevent overfitting)
|
|
1068
|
+
)
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
| Parameter | Type | Default | Recommended Range | Description |
|
|
1072
|
+
|---|---|---|---|---|
|
|
1073
|
+
| `lr` | Float | `0.001` | `1e-4` to `1e-2` | Step size scaling factor along the negative gradient. |
|
|
1074
|
+
| `betas` / `beta1, beta2` | Array / Floats | `[0.9, 0.999]` | `[0.9, 0.999]` | $\beta_1$ tracks momentum; $\beta_2$ tracks squared gradient variance. |
|
|
1075
|
+
| `eps` / `epsilon` | Float | `1e-8` | `1e-8` to `1e-6` | Small constant preventing division by zero. |
|
|
1076
|
+
| `weight_decay` | Float | `0.0` | `1e-5` to `1e-3` | L2 weight shrinkage penalty ($\lambda$) to combat memorization/overfitting. |
|
|
1077
|
+
|
|
1078
|
+
### 2. `GRX::Optim::SGD`
|
|
1079
|
+
Stochastic Gradient Descent with momentum and weight decay:
|
|
1080
|
+
|
|
1081
|
+
```ruby
|
|
1082
|
+
optimizer = GRX::Optim::SGD.new(
|
|
1083
|
+
model.parameters,
|
|
1084
|
+
lr: 0.01, # Learning rate. Recommended: 0.01 to 0.1
|
|
1085
|
+
momentum: 0.9, # Momentum buffer coefficient (mu). Recommended: 0.9
|
|
1086
|
+
weight_decay: 1e-4 # L2 regularization factor
|
|
1087
|
+
)
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
| Parameter | Type | Default | Recommended Range | Description |
|
|
1091
|
+
|---|---|---|---|---|
|
|
1092
|
+
| `lr` | Float | `0.01` | `0.001` to `0.1` | Step size for gradient descent. |
|
|
1093
|
+
| `momentum` | Float | `0.0` | `0.8` to `0.99` | Accumulates gradient velocity in the direction of persistent descent. |
|
|
1094
|
+
| `weight_decay` | Float | `0.0` | `1e-5` to `1e-3` | L2 weight shrinkage penalty. |
|
|
1095
|
+
|
|
1096
|
+
---
|
|
1097
|
+
|
|
1098
|
+
## Data Pipelines & DataLoader (`GRX::Data`)
|
|
1099
|
+
|
|
1100
|
+
### 1. `GRX::Data::TensorDataset`
|
|
1101
|
+
Encapsulates features and labels into an indexed dataset:
|
|
1102
|
+
|
|
1103
|
+
```ruby
|
|
1104
|
+
x_data = GRX.randn([1000, 20])
|
|
1105
|
+
y_data = GRX.tensor(Array.new(1000) { rand(0..2) }, [1000])
|
|
1106
|
+
|
|
1107
|
+
dataset = GRX::Data::TensorDataset.new(x_data, y_data)
|
|
1108
|
+
puts dataset.size # 1000
|
|
1109
|
+
```
|
|
1110
|
+
|
|
1111
|
+
### 2. `GRX::Data::DataLoader`
|
|
1112
|
+
Mini-batch generator with automatic shuffling and slicing:
|
|
1113
|
+
|
|
1114
|
+
```ruby
|
|
1115
|
+
loader = GRX::Data::DataLoader.new(dataset, batch_size: 32, shuffle: true)
|
|
1116
|
+
|
|
1117
|
+
loader.each_with_index do |(batch_x, batch_y), batch_idx|
|
|
1118
|
+
optimizer.zero_grad
|
|
1119
|
+
preds = model.forward(batch_x)
|
|
1120
|
+
loss = criterion.call(preds, batch_y)
|
|
1121
|
+
loss.backward
|
|
1122
|
+
optimizer.step
|
|
1123
|
+
end
|
|
1124
|
+
```
|
|
1125
|
+
|
|
1126
|
+
---
|
|
1127
|
+
|
|
1128
|
+
## Model Persistence & Brain Serialization (`.grx` Format)
|
|
1129
|
+
|
|
1130
|
+
### Binary Format Layout (`GRX1` Specification)
|
|
1131
|
+
|
|
1132
|
+
GRX includes a binary serialization format that dumps floating-point parameters directly from native C memory buffers:
|
|
1133
|
+
|
|
1134
|
+
```text
|
|
1135
|
+
+-------------------+--------------------+---------------------------------------------+
|
|
1136
|
+
| Field | Size | Content |
|
|
1137
|
+
+-------------------+--------------------+---------------------------------------------+
|
|
1138
|
+
| Magic Header | 8 bytes | "GRX1\0\0\0\0" (ASCII + null padding) |
|
|
1139
|
+
| Parameter Count | 4 bytes | uint32 big-endian |
|
|
1140
|
+
+-------------------+--------------------+---------------------------------------------+
|
|
1141
|
+
| For each parameter tensor: |
|
|
1142
|
+
| - Rank | 2 bytes | uint16 big-endian |
|
|
1143
|
+
| - Shape | Rank * 4 bytes | uint32 big-endian array |
|
|
1144
|
+
| - Numel | 8 bytes | uint64 big-endian |
|
|
1145
|
+
| - Data Payload | Numel * 8 bytes | IEEE 754 64-bit Doubles (Direct byte copy) |
|
|
1146
|
+
+-------------------+--------------------+---------------------------------------------+
|
|
1147
|
+
```
|
|
1148
|
+
|
|
1149
|
+
### Production Inference Deployment Workflow
|
|
1150
|
+
|
|
1151
|
+
#### Step 1: Train & Save Brain (`train.rb`)
|
|
1152
|
+
```ruby
|
|
1153
|
+
require "grx"
|
|
1154
|
+
|
|
1155
|
+
model = GRX::NN::Sequential.new(
|
|
1156
|
+
GRX::NN::Linear.new(4, 16),
|
|
1157
|
+
GRX::NN::ReLU.new,
|
|
1158
|
+
GRX::NN::Linear.new(16, 2)
|
|
1159
|
+
)
|
|
1160
|
+
|
|
1161
|
+
# ... (training loop) ...
|
|
1162
|
+
|
|
1163
|
+
# Save trained brain
|
|
1164
|
+
model.save_weights("cerebro_campeon.grx")
|
|
1165
|
+
puts "Brain weights saved to cerebro_campeon.grx"
|
|
1166
|
+
```
|
|
1167
|
+
|
|
1168
|
+
#### Step 2: Load & Serve in Production API (`serve.rb`)
|
|
1169
|
+
```ruby
|
|
1170
|
+
require "grx"
|
|
1171
|
+
|
|
1172
|
+
brain = GRX::NN::Sequential.new(
|
|
1173
|
+
GRX::NN::Linear.new(4, 16),
|
|
1174
|
+
GRX::NN::ReLU.new,
|
|
1175
|
+
GRX::NN::Linear.new(16, 2)
|
|
1176
|
+
)
|
|
1177
|
+
|
|
1178
|
+
# Load binary weights instantly without re-training
|
|
1179
|
+
brain.load_weights("cerebro_campeon.grx")
|
|
1180
|
+
brain.eval!
|
|
1181
|
+
|
|
1182
|
+
# Serve real-time request
|
|
1183
|
+
input_data = GRX.tensor([[0.5, -1.2, 3.4, 0.1]], [1, 4])
|
|
1184
|
+
decision = brain.forward(input_data).softmax.to_a
|
|
1185
|
+
|
|
1186
|
+
puts "Production Decision Probabilities: #{decision.map { |d| d.round(4) }}"
|
|
1187
|
+
```
|
|
1188
|
+
|
|
1189
|
+
---
|
|
1190
|
+
|
|
1191
|
+
## Gradient Management & Utilities (`GRX::Utils`)
|
|
1192
|
+
|
|
1193
|
+
```ruby
|
|
1194
|
+
# 1. Gradient Norm Clipping (prevents exploding gradients)
|
|
1195
|
+
total_norm = GRX::Utils.clip_grad_norm!(model.parameters, max_norm: 1.0)
|
|
1196
|
+
|
|
1197
|
+
# 2. One-Hot Encoding
|
|
1198
|
+
one_hot_matrix = GRX::Utils.one_hot([0, 2, 1], num_classes: 3)
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
---
|
|
1202
|
+
|
|
1203
|
+
## Windows Support & Toolchain Guide
|
|
1204
|
+
|
|
1205
|
+
On Windows operating systems:
|
|
1206
|
+
* **Native C Acceleration (Recommended):** Active automatically when installing via **RubyInstaller with DevKit (MSYS2 / MinGW-w64)**. The native C extension is compiled transparently upon `gem install grx-tensor`.
|
|
1207
|
+
* **Pure Ruby Fallback:** If DevKit is not installed, GRX automatically switches to pure Ruby computation mode without throwing errors.
|
|
1208
|
+
* **Pre-Built Standalone Binaries:** Fat binary gems containing pre-compiled `.dll` files are currently in active development.
|
|
269
1209
|
|
|
270
1210
|
---
|
|
271
1211
|
|
|
272
1212
|
## License
|
|
273
1213
|
|
|
274
|
-
MIT License.
|
|
1214
|
+
MIT License. Copyright (c) 2026 Razo.
|