grydra 1.0.0 → 2.0.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.
data/README.md CHANGED
@@ -1,119 +1,1420 @@
1
- # GRYDRA
1
+ # GRYDRA v2.0 - Neural Networks for Ruby
2
2
 
3
- GRYDRA is a Ruby gem designed for building, training, and utilizing neural networks. It provides a flexible framework for creating multi-subnet architectures, implementing various activation functions, optimization techniques, and preprocessing tools, suitable for numerical, categorical (hash), and text-based data tasks.
3
+ ![Status](https://img.shields.io/badge/Status-Active-brightgreen)
4
+ ![Ruby](https://img.shields.io/badge/Ruby-2.7+-red)
5
+ [![License](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0.html)
4
6
 
5
- ## Features
6
-
7
- * **Neural Network Architectures:** Build standard feedforward networks, multi-subnet ensembles (`MainNetwork`), and simplified interfaces (`EasyNetwork`).
8
- * **Multiple Activation Functions:** Includes Tanh, Sigmoid, ReLU, Leaky ReLU, Swish, GELU, and more.
9
- * **Advanced Optimizers:** Supports the Adam optimizer for efficient training.
10
- * **Regularization Techniques:** Includes L1, L2 regularization, and Dropout.
11
- * **Data Preprocessing:** Offers Min-Max and Z-Score normalization. Includes utilities for handling categorical (hash) and text data (vocabulary creation, vectorization, TF-IDF).
12
- * **Training Features:** Supports mini-batch training, early stopping, learning rate decay, and customizable parameters.
13
- * **Evaluation Metrics:** Provides MSE, MAE, Accuracy, Precision, Recall, F1-Score, Confusion Matrix, and AUC-ROC.
14
- * **Cross-Validation:** Includes k-fold cross-validation for robust model evaluation.
15
- * **Model Persistence:** Save and load trained models and vocabularies using Ruby's `Marshal`.
16
- * **Analysis Tools:** Gradient analysis and ASCII visualization of network architecture.
17
- * **Hyperparameter Search:** Basic grid search functionality.
7
+ A complete, modular, and powerful neural network library for Ruby.
18
8
 
19
9
  ## Installation
20
10
 
21
- 1. Ensure you have Ruby installed.
22
- 2. Save the provided code as a `.rb` file (e.g., `grydra.rb`) or create a Ruby gem.
23
- 3. Require the file or gem in your project: `require 'grydra'` (or the path to your file).
11
+ ```bash
12
+ gem install grydra
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```ruby
18
+ require 'grydra'
19
+
20
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: true)
21
+ data_input = [[1, 2], [2, 3], [3, 4], [4, 5]]
22
+ data_output = [[3], [5], [7], [9]]
23
+
24
+ model.train_numerical(data_input, data_output, [[4, 1]], 0.1, 1000, :max)
25
+ predictions = model.predict_numerical([[5, 6]])
26
+ puts predictions # => [[11.0]]
27
+ ```
28
+
29
+ ## Examples - From Simple to Complex
24
30
 
25
- ## Usage
31
+ ### Level 1: Basic Examples
26
32
 
27
- ### Basic Example: Training with Numerical Data
33
+ #### Example 1.1: Simple Addition
28
34
 
29
35
  ```ruby
30
36
  require 'grydra'
31
37
 
32
- # Example data: [height (cm), age (years)] -> [weight (kg)]
33
- data_input = [[170, 25], [160, 30], [180, 22]]
34
- data_output = [[65], [60], [75]]
38
+ # Learn to add two numbers
39
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
35
40
 
36
- # Define subnet structures (hidden layers)
37
- structures = [[4, 1], [3, 1]]
41
+ data_input = [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]
42
+ data_output = [[2], [4], [6], [8], [10]]
38
43
 
39
- # Create the network using the easy interface
40
- network = GRYDRA::EasyNetwork.new(print_epochs = true)
44
+ model.train_numerical(data_input, data_output, [[3, 1]], 0.1, 500, :max)
41
45
 
42
- # Train the network
43
- network.train_numerical(
44
- data_input,
45
- data_output,
46
- structures,
47
- learning_rate = 0.05,
48
- epochs = 15000,
49
- normalization = :max # Options: :max, :zscore
50
- )
46
+ # Test
47
+ result = model.predict_numerical([[6, 6]])
48
+ puts "6 + 6 = #{result[0][0].round(0)}" # => 12
49
+ ```
50
+
51
+ #### Example 1.2: Temperature Conversion (Celsius to Fahrenheit)
52
+
53
+ ```ruby
54
+ require 'grydra'
55
+
56
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
57
+
58
+ # Training data: Celsius -> Fahrenheit
59
+ celsius = [[0], [10], [20], [30], [40], [100]]
60
+ fahrenheit = [[32], [50], [68], [86], [104], [212]]
61
+
62
+ model.train_numerical(celsius, fahrenheit, [[3, 1]], 0.1, 1000, :max)
63
+
64
+ # Convert 25°C to Fahrenheit
65
+ result = model.predict_numerical([[25]])
66
+ puts "25°C = #{result[0][0].round(1)}°F" # => ~77°F
67
+ ```
68
+
69
+ #### Example 1.3: Simple Classification (Pass/Fail)
70
+
71
+ ```ruby
72
+ require 'grydra'
73
+
74
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
75
+
76
+ # Student scores -> Pass (1) or Fail (0)
77
+ scores = [[45], [55], [65], [75], [85], [35], [95], [40]]
78
+ results = [[0], [1], [1], [1], [1], [0], [1], [0]]
79
+
80
+ model.train_numerical(scores, results, [[3, 1]], 0.1, 1000, :max)
81
+
82
+ # Predict for score 70
83
+ prediction = model.predict_numerical([[70]])
84
+ pass_fail = prediction[0][0] > 0.5 ? "PASS" : "FAIL"
85
+ puts "Score 70: #{pass_fail}"
86
+ ```
87
+
88
+ ### Level 2: Intermediate Examples
89
+
90
+ #### Example 2.1: House Price Prediction
91
+
92
+ ```ruby
93
+ require 'grydra'
94
+
95
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
96
+ model.configure_adam_optimizer(alpha: 0.001)
97
+
98
+ # [sqft, bedrooms, age]
99
+ houses = [
100
+ [1200, 2, 5], [1500, 3, 3], [1800, 3, 8],
101
+ [2000, 4, 2], [2200, 4, 10], [1000, 2, 15]
102
+ ]
103
+ prices = [[250000], [300000], [280000], [350000], [320000], [200000]]
104
+
105
+ model.train_numerical(houses, prices, [[6, 4, 1]], 0.05, 2000, :max,
106
+ lambda_l2: 0.001, patience: 100)
107
+
108
+ # Predict price for 1600 sqft, 3 bed, 4 years old
109
+ new_house = [[1600, 3, 4]]
110
+ price = model.predict_numerical(new_house)
111
+ puts "Predicted price: $#{price[0][0].round(0)}"
112
+ ```
113
+
114
+
115
+ #### Example 2.2: Customer Churn Prediction
116
+
117
+ ```ruby
118
+ require 'grydra'
119
+
120
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
121
+
122
+ # [age, monthly_spend, months_active, support_tickets]
123
+ customers = [
124
+ [25, 50, 3, 5], [45, 200, 24, 0], [35, 100, 12, 2],
125
+ [55, 180, 36, 1], [28, 40, 2, 8], [50, 190, 30, 0]
126
+ ]
127
+ churn = [[1], [0], [0], [0], [1], [0]] # 1=will churn, 0=will stay
128
+
129
+ model.train_numerical(customers, churn, [[6, 4, 1]], 0.1, 1500, :zscore,
130
+ dropout: true, dropout_rate: 0.3)
131
+
132
+ # Predict for new customer
133
+ new_customer = [[30, 75, 6, 3]]
134
+ probability = model.predict_numerical(new_customer, :zscore)
135
+ risk = probability[0][0] > 0.5 ? "HIGH RISK" : "LOW RISK"
136
+ puts "Churn probability: #{(probability[0][0] * 100).round(1)}% - #{risk}"
137
+ ```
138
+
139
+ #### Example 2.3: Sentiment Analysis
140
+
141
+ ```ruby
142
+ require 'grydra'
143
+
144
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
145
+
146
+ texts = [
147
+ "I love this product", "Amazing quality",
148
+ "Terrible experience", "Waste of money",
149
+ "Highly recommend", "Very disappointed",
150
+ "Excellent service", "Poor quality"
151
+ ]
152
+ sentiments = [[1], [1], [0], [0], [1], [0], [1], [0]]
153
+
154
+ model.train_text(texts, sentiments, [[8, 4, 1]], 0.1, 1000, :max,
155
+ lambda_l1: 0.001, dropout: true, dropout_rate: 0.2)
156
+
157
+ # Analyze new reviews
158
+ new_reviews = ["Best purchase ever", "Complete garbage"]
159
+ predictions = model.predict_text(new_reviews, :max)
160
+
161
+ new_reviews.each_with_index do |review, i|
162
+ score = predictions[i][0]
163
+ sentiment = score > 0.5 ? "POSITIVE" : "NEGATIVE"
164
+ puts "\"#{review}\" => #{sentiment}"
165
+ end
166
+ ```
167
+
168
+ ### Level 3: Advanced Examples
169
+
170
+ #### Example 3.1: Multi-Output Prediction (Weather)
171
+
172
+ ```ruby
173
+ require 'grydra'
174
+
175
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
176
+
177
+ # [hour, latitude, longitude] -> [temperature, humidity]
178
+ conditions = [
179
+ [6, 40.7, -74.0], [12, 40.7, -74.0], [18, 40.7, -74.0],
180
+ [6, 34.0, -118.2], [12, 34.0, -118.2], [18, 34.0, -118.2]
181
+ ]
182
+ weather = [[15, 70], [25, 50], [20, 60], [18, 40], [30, 30], [25, 35]]
183
+
184
+ model.train_numerical(conditions, weather, [[6, 4, 2]], 0.05, 2000, :max)
185
+
186
+ # Predict weather at 2 PM in New York
187
+ prediction = model.predict_numerical([[14, 40.7, -74.0]])
188
+ puts "Temperature: #{prediction[0][0].round(1)}°C"
189
+ puts "Humidity: #{prediction[0][1].round(1)}%"
190
+ ```
191
+
192
+ #### Example 3.2: Time Series Prediction (Stock Prices)
193
+
194
+ ```ruby
195
+ require 'grydra'
196
+
197
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
198
+ model.configure_adam_optimizer(alpha: 0.001)
199
+
200
+ # Last 3 days -> Next day price
201
+ sequences = [
202
+ [100, 102, 101], [102, 101, 103], [101, 103, 105],
203
+ [103, 105, 104], [105, 104, 106], [104, 106, 108]
204
+ ]
205
+ next_prices = [[103], [105], [104], [106], [108], [107]]
206
+
207
+ model.train_numerical(sequences, next_prices, [[6, 4, 1]], 0.01, 2000, :max,
208
+ lambda_l2: 0.01, patience: 100)
209
+
210
+ # Predict next price
211
+ last_3_days = [[106, 108, 107]]
212
+ prediction = model.predict_numerical(last_3_days)
213
+ puts "Predicted next price: $#{prediction[0][0].round(2)}"
214
+ ```
215
+
216
+ #### Example 3.3: Image Classification (Simplified)
217
+
218
+ ```ruby
219
+ require 'grydra'
220
+
221
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
222
+
223
+ # Simplified 4x4 "images" (16 pixels) -> 2 classes
224
+ images = [
225
+ [1,1,0,0, 1,1,0,0, 0,0,0,0, 0,0,0,0], # Pattern A
226
+ [0,0,1,1, 0,0,1,1, 0,0,0,0, 0,0,0,0], # Pattern B
227
+ [1,1,0,0, 1,1,0,0, 0,0,0,0, 0,0,0,0], # Pattern A
228
+ [0,0,1,1, 0,0,1,1, 0,0,0,0, 0,0,0,0] # Pattern B
229
+ ]
230
+ labels = [[0], [1], [0], [1]]
231
+
232
+ model.train_numerical(images, labels, [[8, 4, 1]], 0.1, 1000, :max)
233
+
234
+ # Classify new image
235
+ new_image = [[1,1,0,0, 1,1,0,0, 0,0,0,0, 0,0,0,0]]
236
+ prediction = model.predict_numerical(new_image)
237
+ class_label = prediction[0][0] > 0.5 ? "Pattern B" : "Pattern A"
238
+ puts "Classification: #{class_label}"
239
+ ```
240
+
241
+ ### Level 4: Expert Examples
242
+
243
+ #### Example 4.1: Cross-Validation
244
+
245
+ ```ruby
246
+ require 'grydra'
247
+
248
+ # Generate data
249
+ synthetic = GRYDRA::Preprocessing::Data.generate_synthetic_data(100, 3, 0.1, 42)
250
+ data_x = synthetic[:data]
251
+ data_y = synthetic[:labels]
252
+
253
+ # 5-fold cross-validation
254
+ result = GRYDRA::Training::CrossValidation.cross_validation(data_x, data_y, 5) do |train_x, train_y, test_x, test_y|
255
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
256
+ model.train_numerical(train_x, train_y, [[4, 3, 1]], 0.1, 500, :max, patience: 50)
257
+
258
+ predictions = model.predict_numerical(test_x)
259
+ GRYDRA::Metrics.mse(predictions.flatten, test_y.flatten)
260
+ end
261
+
262
+ puts "Cross-Validation Results:"
263
+ puts " Average Error: #{result[:average].round(6)}"
264
+ puts " Std Deviation: #{result[:deviation].round(6)}"
265
+ puts " Fold Errors: #{result[:errors].map { |e| e.round(4) }}"
266
+ ```
267
+
268
+
269
+ #### Example 4.2: Hyperparameter Search
270
+
271
+ ```ruby
272
+ require 'grydra'
273
+
274
+ data_x = [[1], [2], [3], [4], [5], [6], [7], [8]]
275
+ data_y = [[2], [4], [6], [8], [10], [12], [14], [16]]
276
+
277
+ param_grid = [
278
+ { rate: 0.01, epochs: 800, lambda_l2: 0.001 },
279
+ { rate: 0.05, epochs: 600, lambda_l2: 0.01 },
280
+ { rate: 0.1, epochs: 500, lambda_l2: 0.001 }
281
+ ]
51
282
 
52
- # Make a prediction for a new individual
53
- new_data = [[172, 26]]
54
- predictions = network.predict_numerical(new_data, :max) # Use same normalization
283
+ best = GRYDRA::Training::HyperparameterSearch.hyperparameter_search(
284
+ data_x, data_y, param_grid, verbose: true
285
+ ) do |params, x, y|
286
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
287
+ model.train_numerical(x, y, [[3, 1]], params[:rate], params[:epochs], :max,
288
+ lambda_l2: params[:lambda_l2], patience: 50)
289
+
290
+ predictions = model.predict_numerical(x)
291
+ GRYDRA::Metrics.mse(predictions.flatten, y.flatten)
292
+ end
55
293
 
56
- puts "Predicted weight: #{predictions[0][0].round(2)} kg"
294
+ puts "\nBest parameters: #{best[:parameters]}"
295
+ puts "Best score: #{best[:score].round(6)}"
57
296
  ```
58
297
 
59
- ### Basic Example: Training with Hash Data
298
+ #### Example 4.3: PCA for Dimensionality Reduction
60
299
 
61
300
  ```ruby
62
301
  require 'grydra'
63
302
 
64
- # Example data: Categorical inputs mapped to a numerical label
65
- data_hash = [
66
- { height: 170, is_new: false, label: 0 },
67
- { height: 160, is_new: true, label: 1 },
68
- { height: 180, is_new: false, label: 0 },
303
+ # High-dimensional data (5 features)
304
+ data = [
305
+ [2.5, 2.4, 1.2, 0.8, 3.1],
306
+ [0.5, 0.7, 0.3, 0.2, 0.9],
307
+ [2.2, 2.9, 1.5, 1.0, 2.8],
308
+ [1.9, 2.2, 1.0, 0.7, 2.5],
309
+ [3.1, 3.0, 1.8, 1.2, 3.5]
69
310
  ]
70
311
 
71
- input_keys = [:height, :is_new]
72
- label_key = :label
73
- structures = [[3, 1]]
312
+ # Reduce to 2 dimensions
313
+ pca_result = GRYDRA::Preprocessing::PCA.pca(data, components: 2)
314
+
315
+ puts "PCA Results:"
316
+ puts " Explained Variance: #{pca_result[:explained_variance].map { |v| (v * 100).round(2) }}%"
317
+ puts " Eigenvalues: #{pca_result[:eigenvalues].map { |v| v.round(4) }}"
318
+
319
+ # Transform new data
320
+ new_data = [[2.0, 2.5, 1.1, 0.8, 2.7]]
321
+ transformed = GRYDRA::Preprocessing::PCA.transform(new_data, pca_result)
322
+ puts " Transformed: #{transformed[0].map { |v| v.round(3) }}"
323
+ ```
324
+
325
+ #### Example 4.4: Ensemble with Multiple Subnets
326
+
327
+ ```ruby
328
+ require 'grydra'
329
+
330
+ # Create ensemble network
331
+ network = GRYDRA::Networks::MainNetwork.new(print_epochs: false)
332
+
333
+ # Add multiple subnets with different architectures
334
+ network.add_subnet([2, 4, 1], [:tanh, :sigmoid])
335
+ network.add_subnet([2, 3, 1], [:relu, :tanh])
336
+ network.add_subnet([2, 5, 1], [:sigmoid, :sigmoid])
337
+
338
+ # XOR problem
339
+ inputs = [[0, 0], [0, 1], [1, 0], [1, 1]]
340
+ outputs = [[0], [1], [1], [0]]
341
+
342
+ # Train all subnets
343
+ network.train_subnets(
344
+ [
345
+ {input: inputs, output: outputs},
346
+ {input: inputs, output: outputs},
347
+ {input: inputs, output: outputs}
348
+ ],
349
+ 0.5, 3000, patience: 150
350
+ )
351
+
352
+ # Test with ensemble
353
+ puts "XOR Results (Ensemble):"
354
+ inputs.each do |input|
355
+ output = network.combine_results(input)
356
+ result = output[0] > 0.5 ? 1 : 0
357
+ puts " #{input} => #{output[0].round(3)} (#{result})"
358
+ end
359
+ ```
360
+
361
+ #### Example 4.5: Save and Load Models
362
+
363
+ ```ruby
364
+ require 'grydra'
365
+
366
+ # Train model
367
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
368
+ data_input = [[1, 2], [2, 3], [3, 4], [4, 5]]
369
+ data_output = [[3], [5], [7], [9]]
370
+ model.train_numerical(data_input, data_output, [[4, 1]], 0.1, 1000, :max)
371
+
372
+ # Save model
373
+ GRYDRA::Utils::Persistence.save_model(model, "my_model", "./models")
374
+
375
+ # Later... load and use
376
+ loaded_model = GRYDRA::Utils::Persistence.load_model("my_model", "./models")
377
+ prediction = loaded_model.predict_numerical([[5, 6]])
378
+ puts "Prediction: #{prediction[0][0].round(2)}"
379
+
380
+ # Show model summary
381
+ GRYDRA::Utils::Persistence.summary_model(loaded_model)
382
+ ```
383
+
384
+ #### Example 4.6: Using Different Loss Functions
385
+
386
+ ```ruby
387
+ require 'grydra'
388
+
389
+ predictions = [0.9, 0.2, 0.8, 0.1, 0.95]
390
+ targets = [1, 0, 1, 0, 1]
391
+
392
+ puts "Loss Function Comparison:"
393
+ puts " MSE: #{GRYDRA::Losses.mse(predictions, targets).round(4)}"
394
+ puts " MAE: #{GRYDRA::Losses.mae(predictions, targets).round(4)}"
395
+ puts " Huber: #{GRYDRA::Losses.huber(predictions, targets, delta: 1.0).round(4)}"
396
+ puts " Binary Cross-Entropy: #{GRYDRA::Losses.binary_crossentropy(predictions, targets).round(4)}"
397
+ puts " Log-Cosh: #{GRYDRA::Losses.log_cosh(predictions, targets).round(4)}"
398
+ ```
399
+
400
+ #### Example 4.7: Network Visualization
401
+
402
+ ```ruby
403
+ require 'grydra'
404
+
405
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
406
+ data_input = [[1, 2], [2, 3], [3, 4]]
407
+ data_output = [[3], [5], [7]]
408
+ model.train_numerical(data_input, data_output, [[4, 3, 1]], 0.1, 500, :max)
409
+
410
+ # Visualize architecture
411
+ GRYDRA::Utils::Visualization.plot_architecture_ascii(model.network)
412
+
413
+ # Analyze gradients
414
+ analysis = GRYDRA::Utils::Visualization.analyze_gradients(model.network)
415
+ puts "\nGradient Analysis:"
416
+ puts " Average: #{analysis[:average].round(6)}"
417
+ puts " Max: #{analysis[:maximum].round(6)}"
418
+ puts " Min: #{analysis[:minimum].round(6)}"
419
+ puts " Total Parameters: #{analysis[:total_parameters]}"
420
+ ```
421
+
422
+ ### Level 5: Expert Advanced Examples
423
+
424
+ #### Example 5.1: Custom Network with Different Optimizers
425
+
426
+ ```ruby
427
+ require 'grydra'
428
+
429
+ # Compare different optimizers on the same problem
430
+ data_x = Array.new(50) { |i| [i / 10.0] }
431
+ data_y = data_x.map { |x| [Math.sin(x[0]) * 10 + 5] }
432
+
433
+ optimizers = {
434
+ 'Adam' => GRYDRA::Optimizers::Adam.new(alpha: 0.01),
435
+ 'SGD with Momentum' => GRYDRA::Optimizers::SGD.new(learning_rate: 0.1, momentum: 0.9),
436
+ 'RMSprop' => GRYDRA::Optimizers::RMSprop.new(learning_rate: 0.01),
437
+ 'AdaGrad' => GRYDRA::Optimizers::AdaGrad.new(learning_rate: 0.1),
438
+ 'AdamW' => GRYDRA::Optimizers::AdamW.new(alpha: 0.01, weight_decay: 0.01)
439
+ }
440
+
441
+ optimizers.each do |name, optimizer|
442
+ network = GRYDRA::Networks::NeuralNetwork.new([1, 8, 8, 1], activations: [:relu, :relu, :tanh])
443
+ network.instance_variable_set(:@optimizer, optimizer)
444
+
445
+ network.train(data_x, data_y, 0.01, 500, patience: 100)
446
+
447
+ test_x = [[2.5]]
448
+ prediction = network.calculate_outputs(test_x[0])
449
+ actual = Math.sin(2.5) * 10 + 5
450
+ error = (prediction[0] - actual).abs
451
+
452
+ puts "#{name}: Prediction=#{prediction[0].round(3)}, Actual=#{actual.round(3)}, Error=#{error.round(3)}"
453
+ end
454
+ ```
455
+
456
+ #### Example 5.2: Using Callbacks for Advanced Training Control
457
+
458
+ ```ruby
459
+ require 'grydra'
460
+
461
+ # Create network
462
+ network = GRYDRA::Networks::NeuralNetwork.new([2, 6, 4, 1],
463
+ print_epochs: true,
464
+ activations: [:relu, :relu, :sigmoid])
74
465
 
75
- network = GRYDRA::EasyNetwork.new(print_epochs = true)
466
+ # XOR problem
467
+ inputs = [[0, 0], [0, 1], [1, 0], [1, 1]]
468
+ outputs = [[0], [1], [1], [0]]
76
469
 
77
- network.train_hashes(
78
- data_hash,
79
- input_keys,
80
- label_key,
81
- structures,
82
- learning_rate = 0.05,
83
- epochs = 10000,
84
- normalization = :max
470
+ # Setup callbacks
471
+ early_stopping = GRYDRA::Callbacks::EarlyStopping.new(
472
+ monitor: :loss,
473
+ patience: 50,
474
+ min_delta: 0.001,
475
+ restore_best: true
85
476
  )
86
477
 
87
- # Predict for new data
88
- new_hashes = [{ height: 175, is_new: true }]
89
- predictions = network.predict_hashes(new_hashes, input_keys, :max)
478
+ lr_scheduler = GRYDRA::Callbacks::LearningRateScheduler.new do |epoch, current_lr|
479
+ # Reduce learning rate every 100 epochs
480
+ epoch % 100 == 0 && epoch > 0 ? current_lr * 0.9 : current_lr
481
+ end
482
+
483
+ reduce_lr = GRYDRA::Callbacks::ReduceLROnPlateau.new(
484
+ monitor: :loss,
485
+ factor: 0.5,
486
+ patience: 30,
487
+ min_lr: 1e-6
488
+ )
489
+
490
+ csv_logger = GRYDRA::Callbacks::CSVLogger.new('training_log.csv')
491
+
492
+ # Train with callbacks (manual implementation for demonstration)
493
+ network.train(inputs, outputs, 0.5, 1000, patience: 100)
494
+
495
+ puts "\n✅ Training completed with callbacks"
496
+ puts "Check 'training_log.csv' for detailed logs"
497
+ ```
498
+
499
+ #### Example 5.3: Building Custom Architecture with Layers
500
+
501
+ ```ruby
502
+ require 'grydra'
503
+
504
+ # Build a custom network manually using layers
505
+ class CustomNetwork
506
+ attr_accessor :layers
507
+
508
+ def initialize
509
+ @layers = []
510
+ # Input: 10 features
511
+ @layers << GRYDRA::Layers::Dense.new(16, 10, :relu)
512
+ @layers << GRYDRA::Layers::Dense.new(8, 16, :leaky_relu)
513
+ @layers << GRYDRA::Layers::Dense.new(4, 8, :swish)
514
+ @layers << GRYDRA::Layers::Dense.new(1, 4, :sigmoid)
515
+ end
516
+
517
+ def forward(input, dropout: false, dropout_rate: 0.3)
518
+ output = input
519
+ @layers.each_with_index do |layer, idx|
520
+ # Apply dropout to hidden layers only
521
+ apply_drop = dropout && idx < @layers.size - 1
522
+ output = layer.calculate_outputs(output, apply_drop, dropout_rate)
523
+ end
524
+ output
525
+ end
526
+ end
527
+
528
+ # Create and test custom network
529
+ custom_net = CustomNetwork.new
530
+ input = Array.new(10) { rand }
531
+ output = custom_net.forward(input, dropout: true)
532
+
533
+ puts "Custom Network Output: #{output.map { |v| v.round(4) }}"
534
+ puts "Network has #{custom_net.layers.size} layers"
535
+ custom_net.layers.each_with_index do |layer, i|
536
+ puts " Layer #{i + 1}: #{layer.neurons.size} neurons, activation: #{layer.activation}"
537
+ end
538
+ ```
539
+
540
+ #### Example 5.4: Multi-Task Learning with Shared Layers
541
+
542
+ ```ruby
543
+ require 'grydra'
544
+
545
+ # Simulate multi-task learning: predict both price and category
546
+ # [sqft, bedrooms, location_score] -> [price, category]
547
+
548
+ houses = [
549
+ [1200, 2, 0.7], [1500, 3, 0.8], [1800, 3, 0.9],
550
+ [2000, 4, 0.85], [2200, 4, 0.95], [1000, 2, 0.6]
551
+ ]
552
+
553
+ # Task 1: Price (regression)
554
+ prices = [[250], [300], [280], [350], [320], [200]]
555
+
556
+ # Task 2: Category (classification: 0=budget, 1=luxury)
557
+ categories = [[0], [0], [1], [1], [1], [0]]
558
+
559
+ # Create two networks with similar architecture
560
+ price_model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
561
+ category_model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
562
+
563
+ # Train both tasks
564
+ puts "Training price prediction model..."
565
+ price_model.train_numerical(houses, prices, [[8, 6, 1]], 0.05, 1500, :zscore, lambda_l2: 0.001)
90
566
 
91
- puts "Prediction: #{predictions[0][0].round(3)}"
567
+ puts "Training category classification model..."
568
+ category_model.train_numerical(houses, categories, [[8, 4, 1]], 0.1, 1500, :zscore, dropout: true)
569
+
570
+ # Predict on new house
571
+ new_house = [[1600, 3, 0.75]]
572
+ predicted_price = price_model.predict_numerical(new_house, :zscore)
573
+ predicted_category = category_model.predict_numerical(new_house, :zscore)
574
+
575
+ puts "\n🏠 Multi-Task Prediction:"
576
+ puts " Predicted Price: $#{predicted_price[0][0].round(0)}k"
577
+ puts " Category: #{predicted_category[0][0] > 0.5 ? 'Luxury' : 'Budget'} (#{(predicted_category[0][0] * 100).round(1)}%)"
578
+ ```
579
+
580
+ #### Example 5.5: Time Series with LSTM Layer
581
+
582
+ ```ruby
583
+ require 'grydra'
584
+
585
+ # Create LSTM layer for sequence processing
586
+ lstm = GRYDRA::Layers::LSTM.new(units: 8, inputs_per_unit: 3, return_sequences: false)
587
+
588
+ # Time series data: [day1, day2, day3] -> predict day4
589
+ sequences = [
590
+ [[100], [102], [101]],
591
+ [[102], [101], [103]],
592
+ [[101], [103], [105]],
593
+ [[103], [105], [104]]
594
+ ]
595
+
596
+ puts "LSTM Layer Processing:"
597
+ sequences.each_with_index do |seq, i|
598
+ lstm.reset_state # Reset for each sequence
599
+ output = lstm.calculate_outputs(seq)
600
+ puts " Sequence #{i + 1}: #{seq.flatten} => Hidden State: #{output.map { |v| v.round(3) }}"
601
+ end
92
602
  ```
93
603
 
94
- ### Key Classes
604
+ #### Example 5.6: Advanced Ensemble with Weighted Voting
605
+
606
+ ```ruby
607
+ require 'grydra'
95
608
 
96
- * `GRYDRA::EasyNetwork`: A high-level interface for easier training and prediction on numerical, hash, and text data.
97
- * `GRYDRA::MainNetwork`: A class for managing and training multiple sub-networks.
98
- * `GRYDRA::NeuralNetwork`: Represents a single neural network (used internally by `MainNetwork`).
99
- * `GRYDRA::Neuron`: Represents a single neuron within a layer.
100
- * `GRYDRA::DenseLayer`: A standard fully connected layer.
101
- * `GRYDRA::AdamOptimizer`: An implementation of the Adam optimizer.
609
+ # Create ensemble with different architectures and activations
610
+ ensemble = GRYDRA::Networks::MainNetwork.new(print_epochs: false)
102
611
 
103
- ### Helper Functions
612
+ # Add diverse subnets
613
+ ensemble.add_subnet([2, 8, 4, 1], [:relu, :relu, :sigmoid])
614
+ ensemble.add_subnet([2, 6, 3, 1], [:tanh, :tanh, :tanh])
615
+ ensemble.add_subnet([2, 10, 5, 1], [:leaky_relu, :swish, :sigmoid])
616
+ ensemble.add_subnet([2, 4, 4, 1], [:gelu, :relu, :sigmoid])
104
617
 
105
- * `GRYDRA.save_model(model, name, path, vocabulary)`: Saves a trained model.
106
- * `GRYDRA.load_model(name, path)`: Loads a saved model.
107
- * `GRYDRA.save_vocabulary(vocabulary, name, path)`: Saves a vocabulary.
108
- * `GRYDRA.load_vocabulary(name, path)`: Loads a vocabulary.
109
- * `GRYDRA.describe_method(class_name, method_name)`: Provides information and examples for specific methods.
110
- * `GRYDRA.list_methods_available()`: Lists all documented public methods.
111
- * `GRYDRA.generate_example(num, filename, ext, path)`: Generates example scripts demonstrating usage.
618
+ # XOR problem
619
+ inputs = [[0, 0], [0, 1], [1, 0], [1, 1]]
620
+ outputs = [[0], [1], [1], [0]]
112
621
 
113
- ## Examples
622
+ # Train all subnets
623
+ data = Array.new(4) { { input: inputs, output: outputs } }
624
+ ensemble.train_subnets(data, 0.7, 2000, patience: 200)
625
+
626
+ # Test with different voting strategies
627
+ puts "\n🎯 Ensemble Predictions:"
628
+ inputs.each do |input|
629
+ # Average voting
630
+ avg_output = ensemble.combine_results(input)
631
+
632
+ # Weighted voting (give more weight to better performing models)
633
+ weights = [0.4, 0.3, 0.2, 0.1]
634
+ weighted_output = ensemble.combine_results_weighted(input, weights)
635
+
636
+ puts "Input: #{input}"
637
+ puts " Average: #{avg_output[0].round(3)} => #{avg_output[0] > 0.5 ? 1 : 0}"
638
+ puts " Weighted: #{weighted_output[0].round(3)} => #{weighted_output[0] > 0.5 ? 1 : 0}"
639
+ end
640
+ ```
641
+
642
+ #### Example 5.7: Real-World: Credit Card Fraud Detection
643
+
644
+ ```ruby
645
+ require 'grydra'
646
+
647
+ # Simulate credit card transaction data
648
+ # [amount, time_of_day, merchant_category, distance_from_home, frequency]
649
+ transactions = [
650
+ [50, 14, 1, 2, 5], # Normal
651
+ [30, 10, 2, 1, 8], # Normal
652
+ [5000, 3, 5, 500, 1], # Fraud
653
+ [100, 18, 3, 5, 6], # Normal
654
+ [2000, 2, 4, 300, 1], # Fraud
655
+ [75, 12, 1, 3, 7], # Normal
656
+ [3500, 4, 5, 450, 1], # Fraud
657
+ [45, 16, 2, 2, 9] # Normal
658
+ ]
659
+
660
+ labels = [[0], [0], [1], [0], [1], [0], [1], [0]] # 0=normal, 1=fraud
661
+
662
+ # Create model with class imbalance handling
663
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: true)
664
+ model.configure_adam_optimizer(alpha: 0.001)
665
+
666
+ # Train with heavy regularization and dropout
667
+ model.train_numerical(transactions, labels, [[10, 8, 4, 1]], 0.05, 2000, :zscore,
668
+ lambda_l1: 0.001, lambda_l2: 0.01,
669
+ dropout: true, dropout_rate: 0.4,
670
+ patience: 150)
671
+
672
+ # Test on new transactions
673
+ new_transactions = [
674
+ [60, 15, 1, 2, 6], # Should be normal
675
+ [4000, 3, 5, 400, 1] # Should be fraud
676
+ ]
114
677
 
115
- The `GRYDRA.generate_example` method can create various example scripts (numbered 1-12) showcasing different features like advanced training, text processing, classification metrics, and cross-validation. Run `GRYDRA.generate_example(1)` to start.
678
+ predictions = model.predict_numerical(new_transactions, :zscore)
679
+
680
+ puts "\n💳 Fraud Detection Results:"
681
+ new_transactions.each_with_index do |trans, i|
682
+ prob = predictions[i][0]
683
+ status = prob > 0.5 ? "🚨 FRAUD" : "✅ NORMAL"
684
+ puts "Transaction #{i + 1}: #{trans}"
685
+ puts " Fraud Probability: #{(prob * 100).round(2)}% - #{status}"
686
+ end
687
+ ```
688
+
689
+ #### Example 5.8: A/B Testing with Statistical Validation
690
+
691
+ ```ruby
692
+ require 'grydra'
693
+
694
+ # Simulate A/B test data: [page_views, time_on_site, clicks] -> conversion
695
+ variant_a = [
696
+ [100, 120, 5], [150, 180, 8], [80, 90, 3],
697
+ [120, 150, 6], [90, 100, 4]
698
+ ]
699
+ conversions_a = [[0], [1], [0], [1], [0]]
700
+
701
+ variant_b = [
702
+ [110, 140, 7], [160, 200, 10], [85, 110, 5],
703
+ [130, 170, 8], [95, 120, 6]
704
+ ]
705
+ conversions_b = [[1], [1], [0], [1], [1]]
706
+
707
+ # Train separate models
708
+ model_a = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
709
+ model_b = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
710
+
711
+ model_a.train_numerical(variant_a, conversions_a, [[6, 4, 1]], 0.1, 1000, :max)
712
+ model_b.train_numerical(variant_b, conversions_b, [[6, 4, 1]], 0.1, 1000, :max)
713
+
714
+ # Cross-validate both variants
715
+ puts "📊 A/B Test Results:"
716
+
717
+ result_a = GRYDRA::Training::CrossValidation.cross_validation(variant_a, conversions_a, 3) do |train_x, train_y, test_x, test_y|
718
+ m = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
719
+ m.train_numerical(train_x, train_y, [[6, 4, 1]], 0.1, 500, :max)
720
+ preds = m.predict_numerical(test_x)
721
+ GRYDRA::Metrics.mse(preds.flatten, test_y.flatten)
722
+ end
723
+
724
+ result_b = GRYDRA::Training::CrossValidation.cross_validation(variant_b, conversions_b, 3) do |train_x, train_y, test_x, test_y|
725
+ m = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
726
+ m.train_numerical(train_x, train_y, [[6, 4, 1]], 0.1, 500, :max)
727
+ preds = m.predict_numerical(test_x)
728
+ GRYDRA::Metrics.mse(preds.flatten, test_y.flatten)
729
+ end
730
+
731
+ puts "Variant A - Avg Error: #{result_a[:average].round(4)}, StdDev: #{result_a[:deviation].round(4)}"
732
+ puts "Variant B - Avg Error: #{result_b[:average].round(4)}, StdDev: #{result_b[:deviation].round(4)}"
733
+ puts "Winner: #{result_b[:average] < result_a[:average] ? 'Variant B' : 'Variant A'}"
734
+ ```
735
+
736
+ #### Example 5.9: Product Recommendation System
737
+
738
+ ```ruby
739
+ require 'grydra'
740
+
741
+ # User features: [age, purchase_history, browsing_time, category_preference]
742
+ # Product features: [price_range, popularity, category_match]
743
+ # Combined: [user_features + product_features] -> purchase_probability
744
+
745
+ training_data = [
746
+ [25, 5, 120, 0.8, 1, 0.7, 0.9], # Young user, low price, high match -> buy
747
+ [45, 20, 200, 0.6, 3, 0.9, 0.7], # Mature user, high price, popular -> buy
748
+ [30, 2, 50, 0.3, 1, 0.3, 0.2], # Low engagement, low match -> no buy
749
+ [50, 15, 180, 0.9, 2, 0.8, 0.95], # High engagement, good match -> buy
750
+ [22, 1, 30, 0.2, 1, 0.2, 0.1], # New user, poor match -> no buy
751
+ [35, 10, 150, 0.7, 2, 0.6, 0.8] # Average user, decent match -> buy
752
+ ]
753
+
754
+ labels = [[1], [1], [0], [1], [0], [1]]
755
+
756
+ # Train recommendation model
757
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
758
+ model.configure_adam_optimizer(alpha: 0.001)
759
+
760
+ model.train_numerical(training_data, labels, [[12, 8, 4, 1]], 0.05, 2000, :zscore,
761
+ lambda_l2: 0.001, dropout: true, dropout_rate: 0.2,
762
+ patience: 150)
763
+
764
+ # Recommend products for new user-product pairs
765
+ new_pairs = [
766
+ [28, 3, 90, 0.5, 1, 0.5, 0.6], # Young user, budget product
767
+ [40, 12, 160, 0.8, 3, 0.85, 0.9] # Mature user, premium product
768
+ ]
769
+
770
+ predictions = model.predict_numerical(new_pairs, :zscore)
771
+
772
+ puts "\n🛍️ Product Recommendations:"
773
+ new_pairs.each_with_index do |pair, i|
774
+ prob = predictions[i][0]
775
+ recommendation = prob > 0.5 ? "✅ RECOMMEND" : "❌ DON'T RECOMMEND"
776
+ puts "User-Product Pair #{i + 1}:"
777
+ puts " Purchase Probability: #{(prob * 100).round(2)}%"
778
+ puts " Decision: #{recommendation}"
779
+ end
780
+ ```
781
+
782
+ #### Example 5.10: Anomaly Detection in IoT Sensor Data
783
+
784
+ ```ruby
785
+ require 'grydra'
786
+
787
+ # IoT sensor readings: [temperature, humidity, pressure, vibration, power_consumption]
788
+ # Normal operating conditions
789
+ normal_data = [
790
+ [22.5, 45, 1013, 0.2, 150],
791
+ [23.0, 47, 1012, 0.3, 155],
792
+ [22.8, 46, 1013, 0.25, 152],
793
+ [23.2, 48, 1011, 0.28, 158],
794
+ [22.6, 45, 1014, 0.22, 151],
795
+ [23.1, 47, 1012, 0.29, 156]
796
+ ]
797
+
798
+ # Anomalous conditions
799
+ anomaly_data = [
800
+ [35.0, 80, 980, 2.5, 300], # Overheating
801
+ [15.0, 20, 1050, 0.1, 50], # Underpowered
802
+ [25.0, 50, 1010, 5.0, 200], # High vibration
803
+ [40.0, 90, 970, 3.0, 350] # Multiple issues
804
+ ]
805
+
806
+ # Label data: 0 = normal, 1 = anomaly
807
+ all_data = normal_data + anomaly_data
808
+ labels = [[0]] * normal_data.size + [[1]] * anomaly_data.size
809
+
810
+ # Train anomaly detection model
811
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
812
+ model.configure_adam_optimizer(alpha: 0.001)
813
+
814
+ model.train_numerical(all_data, labels, [[10, 8, 4, 1]], 0.05, 2000, :zscore,
815
+ lambda_l1: 0.001, lambda_l2: 0.01,
816
+ dropout: true, dropout_rate: 0.3,
817
+ patience: 150)
818
+
819
+ # Monitor new sensor readings
820
+ new_readings = [
821
+ [22.9, 46, 1013, 0.26, 153], # Should be normal
822
+ [38.0, 85, 975, 2.8, 320], # Should be anomaly
823
+ [23.5, 49, 1011, 0.31, 159] # Should be normal
824
+ ]
825
+
826
+ predictions = model.predict_numerical(new_readings, :zscore)
827
+
828
+ puts "\n🔍 IoT Anomaly Detection:"
829
+ new_readings.each_with_index do |reading, i|
830
+ prob = predictions[i][0]
831
+ status = prob > 0.5 ? "⚠️ ANOMALY DETECTED" : "✅ NORMAL"
832
+ puts "Sensor Reading #{i + 1}: #{reading}"
833
+ puts " Anomaly Score: #{(prob * 100).round(2)}%"
834
+ puts " Status: #{status}"
835
+ end
836
+ ```
837
+
838
+ #### Example 5.11: Dynamic Pricing Optimization
839
+
840
+ ```ruby
841
+ require 'grydra'
842
+
843
+ # Features: [demand, competitor_price, inventory_level, day_of_week, season, customer_segment]
844
+ # Target: optimal_price_multiplier (0.8 to 1.5)
845
+
846
+ pricing_data = [
847
+ [100, 50, 200, 1, 1, 1], # High demand, weekday, winter, regular -> 1.2x
848
+ [50, 45, 500, 6, 2, 2], # Low demand, weekend, spring, premium -> 0.9x
849
+ [150, 55, 50, 5, 3, 1], # Very high demand, low stock, summer -> 1.4x
850
+ [80, 48, 300, 3, 1, 2], # Medium demand, good stock -> 1.0x
851
+ [30, 40, 600, 7, 4, 3], # Low demand, high stock, fall, budget -> 0.8x
852
+ [120, 52, 100, 2, 3, 1] # High demand, low stock, summer -> 1.3x
853
+ ]
854
+
855
+ price_multipliers = [[1.2], [0.9], [1.4], [1.0], [0.8], [1.3]]
856
+
857
+ # Train pricing model
858
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
859
+ model.configure_adam_optimizer(alpha: 0.001)
860
+
861
+ model.train_numerical(pricing_data, price_multipliers, [[10, 8, 4, 1]], 0.05, 2000, :zscore,
862
+ lambda_l2: 0.001, patience: 150)
863
+
864
+ # Optimize pricing for new scenarios
865
+ new_scenarios = [
866
+ [110, 51, 150, 4, 3, 1], # High demand, Thursday, summer
867
+ [40, 43, 450, 6, 2, 3], # Low demand, Saturday, spring, budget
868
+ [90, 49, 250, 2, 1, 2] # Medium demand, Tuesday, winter, premium
869
+ ]
870
+
871
+ predictions = model.predict_numerical(new_scenarios, :zscore)
872
+
873
+ puts "\n💰 Dynamic Pricing Recommendations:"
874
+ base_price = 100
875
+ new_scenarios.each_with_index do |scenario, i|
876
+ multiplier = predictions[i][0]
877
+ optimal_price = base_price * multiplier
878
+
879
+ puts "Scenario #{i + 1}: Demand=#{scenario[0]}, Competitor=$#{scenario[1]}, Stock=#{scenario[2]}"
880
+ puts " Price Multiplier: #{multiplier.round(2)}x"
881
+ puts " Optimal Price: $#{optimal_price.round(2)} (base: $#{base_price})"
882
+ puts " Strategy: #{multiplier > 1.1 ? 'Premium Pricing' : (multiplier < 0.95 ? 'Discount Pricing' : 'Standard Pricing')}"
883
+ end
884
+ ```
885
+
886
+ #### Example 5.12: Medical Diagnosis Assistant
887
+
888
+ ```ruby
889
+ require 'grydra'
890
+
891
+ # Patient features: [age, blood_pressure, cholesterol, glucose, bmi, family_history, smoking]
892
+ # Diagnosis: 0 = healthy, 1 = at risk
893
+
894
+ patient_data = [
895
+ [45, 120, 180, 90, 22, 0, 0], # Healthy
896
+ [55, 140, 240, 110, 28, 1, 1], # At risk
897
+ [38, 115, 170, 85, 21, 0, 0], # Healthy
898
+ [62, 150, 260, 125, 31, 1, 1], # At risk
899
+ [50, 135, 220, 105, 27, 1, 0], # At risk
900
+ [42, 118, 175, 88, 23, 0, 0], # Healthy
901
+ [58, 145, 250, 120, 30, 1, 1], # At risk
902
+ [40, 122, 185, 92, 24, 0, 0] # Healthy
903
+ ]
904
+
905
+ diagnoses = [[0], [1], [0], [1], [1], [0], [1], [0]]
906
+
907
+ # Train diagnostic model with high accuracy requirements
908
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
909
+ model.configure_adam_optimizer(alpha: 0.001)
910
+
911
+ model.train_numerical(patient_data, diagnoses, [[12, 10, 6, 1]], 0.05, 3000, :zscore,
912
+ lambda_l2: 0.001, dropout: true, dropout_rate: 0.2,
913
+ patience: 200)
914
+
915
+ # Evaluate model performance
916
+ metrics = model.evaluate_model(patient_data, diagnoses, [:accuracy, :confusion_matrix], :zscore)
917
+
918
+ puts "\n🏥 Medical Diagnosis Model Performance:"
919
+ puts " Accuracy: #{(metrics[:accuracy] * 100).round(2)}%"
920
+ if metrics[:confusion_matrix]
921
+ cm = metrics[:confusion_matrix]
922
+ puts " True Positives: #{cm[:tp]}"
923
+ puts " True Negatives: #{cm[:tn]}"
924
+ puts " False Positives: #{cm[:fp]}"
925
+ puts " False Negatives: #{cm[:fn]}"
926
+ end
927
+
928
+ # Diagnose new patients
929
+ new_patients = [
930
+ [48, 125, 195, 95, 25, 0, 0], # Borderline
931
+ [60, 155, 270, 130, 32, 1, 1] # High risk
932
+ ]
933
+
934
+ predictions = model.predict_numerical(new_patients, :zscore)
935
+
936
+ puts "\n👨‍⚕️ New Patient Diagnoses:"
937
+ new_patients.each_with_index do |patient, i|
938
+ risk_score = predictions[i][0]
939
+ risk_level = risk_score > 0.7 ? "HIGH RISK" : (risk_score > 0.3 ? "MODERATE RISK" : "LOW RISK")
940
+
941
+ puts "Patient #{i + 1}: Age=#{patient[0]}, BP=#{patient[1]}, Cholesterol=#{patient[2]}"
942
+ puts " Risk Score: #{(risk_score * 100).round(2)}%"
943
+ puts " Assessment: #{risk_level}"
944
+ puts " Recommendation: #{risk_score > 0.5 ? 'Immediate consultation recommended' : 'Regular monitoring'}"
945
+ end
946
+ ```
947
+
948
+ ## Features
949
+
950
+ ### Core Components
951
+ - **7 Activation Functions**: tanh, relu, sigmoid, leaky_relu, swish, gelu, softmax
952
+ - **8 Loss Functions**: MSE, MAE, Huber, Binary/Categorical Cross-Entropy, Hinge, Log-Cosh, Quantile
953
+ - **5 Optimizers**: Adam, SGD (with momentum/Nesterov), RMSprop, AdaGrad, AdamW
954
+ - **3 Layer Types**: Dense (fully connected), Convolutional, LSTM
955
+ - **6 Training Callbacks**: EarlyStopping, LearningRateScheduler, ReduceLROnPlateau, ModelCheckpoint, CSVLogger, ProgressBar
956
+
957
+ ### Advanced Features
958
+ - **Regularization**: L1 (Lasso), L2 (Ridge), Dropout
959
+ - **Normalization**: Z-score, Min-Max, Feature-wise
960
+ - **Model Validation**: K-fold cross-validation, train/test split
961
+ - **Hyperparameter Tuning**: Grid search with parallel evaluation
962
+ - **Dimensionality Reduction**: PCA with power iteration
963
+ - **Text Processing**: Vocabulary creation, TF-IDF vectorization
964
+ - **Model Persistence**: Save/load models and vocabularies
965
+ - **Visualization**: ASCII architecture plots, gradient analysis, training curves
966
+ - **Ensemble Learning**: Multiple subnets with weighted voting
967
+
968
+ ### Network Architectures
969
+ - **EasyNetwork**: High-level API for quick prototyping
970
+ - **MainNetwork**: Ensemble of multiple subnets
971
+ - **NeuralNetwork**: Low-level customizable architecture
972
+ - **Custom Layers**: Build your own layer types
973
+
974
+ ### Level 6: Production-Ready Examples
975
+
976
+ #### Example 6.1: Email Spam Classifier
977
+
978
+ ```ruby
979
+ require 'grydra'
980
+
981
+ # Email features: word frequencies and metadata
982
+ emails = [
983
+ "free money click here win prize",
984
+ "meeting tomorrow at 3pm",
985
+ "congratulations you won lottery",
986
+ "project deadline next week",
987
+ "claim your prize now limited time",
988
+ "lunch with team on friday"
989
+ ]
990
+
991
+ labels = [[1], [0], [1], [0], [1], [0]] # 1=spam, 0=not spam
992
+
993
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
994
+ model.train_text(emails, labels, [[10, 6, 1]], 0.1, 1500, :max,
995
+ lambda_l1: 0.001, dropout: true, dropout_rate: 0.3)
996
+
997
+ # Test on new emails
998
+ new_emails = [
999
+ "urgent meeting tomorrow morning",
1000
+ "click here for free money now"
1001
+ ]
1002
+
1003
+ predictions = model.predict_text(new_emails, :max)
1004
+ new_emails.each_with_index do |email, i|
1005
+ spam_prob = predictions[i][0]
1006
+ label = spam_prob > 0.5 ? "📧 SPAM" : "✅ LEGITIMATE"
1007
+ puts "\"#{email}\""
1008
+ puts " #{label} (#{(spam_prob * 100).round(1)}% confidence)\n\n"
1009
+ end
1010
+ ```
1011
+
1012
+ #### Example 6.2: Employee Attrition Prediction
1013
+
1014
+ ```ruby
1015
+ require 'grydra'
1016
+
1017
+ # Employee data: [satisfaction, evaluation, projects, hours, tenure, accident, promotion]
1018
+ employees = [
1019
+ [0.38, 0.53, 2, 157, 3, 0, 0], # Left
1020
+ [0.80, 0.86, 5, 262, 6, 0, 0], # Stayed
1021
+ [0.11, 0.88, 7, 272, 4, 0, 0], # Left
1022
+ [0.72, 0.87, 5, 223, 5, 0, 0], # Stayed
1023
+ [0.37, 0.52, 2, 159, 3, 0, 0], # Left
1024
+ [0.41, 0.50, 2, 153, 3, 0, 0], # Left
1025
+ [0.10, 0.77, 6, 247, 4, 0, 0], # Left
1026
+ [0.92, 0.85, 5, 259, 5, 0, 1] # Stayed
1027
+ ]
1028
+
1029
+ attrition = [[1], [0], [1], [0], [1], [1], [1], [0]]
1030
+
1031
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
1032
+ model.configure_adam_optimizer(alpha: 0.001)
1033
+
1034
+ model.train_numerical(employees, attrition, [[12, 8, 4, 1]], 0.05, 2000, :zscore,
1035
+ lambda_l2: 0.001, dropout: true, dropout_rate: 0.25,
1036
+ patience: 150)
1037
+
1038
+ # Predict for new employees
1039
+ new_employees = [
1040
+ [0.45, 0.55, 3, 180, 3, 0, 0], # Moderate risk
1041
+ [0.85, 0.90, 4, 240, 6, 0, 1] # Low risk
1042
+ ]
1043
+
1044
+ predictions = model.predict_numerical(new_employees, :zscore)
1045
+
1046
+ puts "👔 Employee Attrition Predictions:"
1047
+ new_employees.each_with_index do |emp, i|
1048
+ risk = predictions[i][0]
1049
+ status = risk > 0.6 ? "🔴 HIGH RISK" : (risk > 0.3 ? "🟡 MODERATE" : "🟢 LOW RISK")
1050
+ puts "Employee #{i + 1}: Satisfaction=#{emp[0]}, Evaluation=#{emp[1]}, Projects=#{emp[2]}"
1051
+ puts " Attrition Risk: #{(risk * 100).round(1)}% - #{status}"
1052
+ puts " Action: #{risk > 0.5 ? 'Schedule retention interview' : 'Continue monitoring'}\n\n"
1053
+ end
1054
+ ```
1055
+
1056
+ #### Example 6.3: Real Estate Valuation with Multiple Features
1057
+
1058
+ ```ruby
1059
+ require 'grydra'
1060
+
1061
+ # Property features: [sqft, bedrooms, bathrooms, age, lot_size, garage, pool, school_rating]
1062
+ properties = [
1063
+ [1500, 3, 2, 10, 5000, 2, 0, 8],
1064
+ [2200, 4, 3, 5, 7500, 2, 1, 9],
1065
+ [1800, 3, 2, 15, 6000, 1, 0, 7],
1066
+ [2800, 5, 4, 3, 10000, 3, 1, 10],
1067
+ [1200, 2, 1, 25, 4000, 1, 0, 6],
1068
+ [2000, 4, 2.5, 8, 6500, 2, 0, 8],
1069
+ [3200, 5, 4, 2, 12000, 3, 1, 9],
1070
+ [1600, 3, 2, 12, 5500, 2, 0, 7]
1071
+ ]
1072
+
1073
+ prices = [[320], [485], [365], [650], [245], [410], [720], [340]] # in thousands
1074
+
1075
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
1076
+ model.configure_adam_optimizer(alpha: 0.001)
1077
+
1078
+ model.train_numerical(properties, prices, [[16, 12, 8, 1]], 0.03, 3000, :zscore,
1079
+ lambda_l2: 0.001, patience: 200)
1080
+
1081
+ # Evaluate with cross-validation
1082
+ cv_result = GRYDRA::Training::CrossValidation.cross_validation(properties, prices, 4) do |train_x, train_y, test_x, test_y|
1083
+ m = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
1084
+ m.configure_adam_optimizer(alpha: 0.001)
1085
+ m.train_numerical(train_x, train_y, [[16, 12, 8, 1]], 0.03, 1500, :zscore, lambda_l2: 0.001, patience: 100)
1086
+ preds = m.predict_numerical(test_x, :zscore)
1087
+ GRYDRA::Metrics.mae(preds.flatten, test_y.flatten)
1088
+ end
1089
+
1090
+ puts "🏡 Real Estate Model Performance:"
1091
+ puts " Average MAE: $#{cv_result[:average].round(2)}k"
1092
+ puts " Std Deviation: $#{cv_result[:deviation].round(2)}k\n\n"
1093
+
1094
+ # Predict new properties
1095
+ new_properties = [
1096
+ [1900, 3, 2, 7, 6200, 2, 0, 8],
1097
+ [2500, 4, 3, 4, 8000, 2, 1, 9]
1098
+ ]
1099
+
1100
+ predictions = model.predict_numerical(new_properties, :zscore)
1101
+
1102
+ puts "Property Valuations:"
1103
+ new_properties.each_with_index do |prop, i|
1104
+ price = predictions[i][0]
1105
+ puts "Property #{i + 1}: #{prop[0]} sqft, #{prop[1]} bed, #{prop[2]} bath"
1106
+ puts " Estimated Value: $#{price.round(0)}k"
1107
+ puts " Price per sqft: $#{(price * 1000 / prop[0]).round(2)}\n\n"
1108
+ end
1109
+ ```
1110
+
1111
+ #### Example 6.4: Customer Lifetime Value Prediction
1112
+
1113
+ ```ruby
1114
+ require 'grydra'
1115
+
1116
+ # Customer features: [age, income, purchase_freq, avg_order, tenure_months, support_calls, returns]
1117
+ customers = [
1118
+ [28, 45000, 12, 85, 24, 2, 1],
1119
+ [45, 95000, 24, 150, 48, 1, 0],
1120
+ [35, 65000, 18, 110, 36, 3, 2],
1121
+ [52, 120000, 30, 200, 60, 0, 0],
1122
+ [25, 35000, 6, 50, 12, 5, 3],
1123
+ [40, 80000, 20, 130, 42, 2, 1],
1124
+ [55, 110000, 28, 180, 54, 1, 0],
1125
+ [30, 50000, 10, 75, 18, 4, 2]
1126
+ ]
1127
+
1128
+ # Lifetime value in thousands
1129
+ ltv = [[15], [45], [28], [65], [8], [35], [55], [12]]
1130
+
1131
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
1132
+ model.configure_adam_optimizer(alpha: 0.001)
1133
+
1134
+ model.train_numerical(customers, ltv, [[14, 10, 6, 1]], 0.04, 2500, :zscore,
1135
+ lambda_l2: 0.001, dropout: true, dropout_rate: 0.2,
1136
+ patience: 180)
1137
+
1138
+ # Segment customers
1139
+ new_customers = [
1140
+ [32, 55000, 15, 95, 30, 2, 1], # Mid-value
1141
+ [48, 105000, 26, 175, 50, 1, 0], # High-value
1142
+ [26, 38000, 8, 60, 15, 4, 2] # Low-value
1143
+ ]
1144
+
1145
+ predictions = model.predict_numerical(new_customers, :zscore)
1146
+
1147
+ puts "💰 Customer Lifetime Value Predictions:"
1148
+ new_customers.each_with_index do |cust, i|
1149
+ value = predictions[i][0]
1150
+ segment = value > 40 ? "💎 PREMIUM" : (value > 20 ? "⭐ STANDARD" : "📊 BASIC")
1151
+
1152
+ puts "Customer #{i + 1}: Age=#{cust[0]}, Income=$#{cust[1]}, Purchases/yr=#{cust[2]}"
1153
+ puts " Predicted LTV: $#{value.round(1)}k"
1154
+ puts " Segment: #{segment}"
1155
+ puts " Strategy: #{value > 40 ? 'VIP treatment, exclusive offers' : (value > 20 ? 'Regular engagement, loyalty program' : 'Cost-effective retention')}\n\n"
1156
+ end
1157
+ ```
1158
+
1159
+ #### Example 6.5: Network Traffic Anomaly Detection
1160
+
1161
+ ```ruby
1162
+ require 'grydra'
1163
+
1164
+ # Network metrics: [packets/sec, bytes/sec, connections, failed_logins, port_scans]
1165
+ normal_traffic = [
1166
+ [1000, 500000, 50, 0, 0],
1167
+ [1200, 600000, 55, 1, 0],
1168
+ [950, 480000, 48, 0, 0],
1169
+ [1100, 550000, 52, 1, 0],
1170
+ [1050, 520000, 51, 0, 0]
1171
+ ]
1172
+
1173
+ attack_traffic = [
1174
+ [5000, 2500000, 200, 50, 10], # DDoS
1175
+ [800, 400000, 45, 100, 0], # Brute force
1176
+ [1500, 750000, 80, 5, 50], # Port scan
1177
+ [10000, 5000000, 500, 20, 5] # Combined attack
1178
+ ]
1179
+
1180
+ all_traffic = normal_traffic + attack_traffic
1181
+ labels = [[0]] * normal_traffic.size + [[1]] * attack_traffic.size
1182
+
1183
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
1184
+ model.configure_adam_optimizer(alpha: 0.001)
1185
+
1186
+ model.train_numerical(all_traffic, labels, [[10, 8, 4, 1]], 0.05, 2500, :zscore,
1187
+ lambda_l1: 0.001, lambda_l2: 0.01,
1188
+ dropout: true, dropout_rate: 0.35,
1189
+ patience: 180)
1190
+
1191
+ # Monitor live traffic
1192
+ live_traffic = [
1193
+ [1080, 540000, 53, 1, 0], # Normal
1194
+ [4500, 2250000, 180, 45, 8], # Suspicious
1195
+ [1150, 575000, 56, 0, 0] # Normal
1196
+ ]
1197
+
1198
+ predictions = model.predict_numerical(live_traffic, :zscore)
1199
+
1200
+ puts "🔒 Network Security Monitoring:"
1201
+ live_traffic.each_with_index do |traffic, i|
1202
+ threat_level = predictions[i][0]
1203
+ status = threat_level > 0.7 ? "🚨 CRITICAL" : (threat_level > 0.4 ? "⚠️ WARNING" : "✅ NORMAL")
1204
+
1205
+ puts "Traffic Sample #{i + 1}: #{traffic[0]} pkt/s, #{traffic[2]} connections"
1206
+ puts " Threat Score: #{(threat_level * 100).round(1)}%"
1207
+ puts " Status: #{status}"
1208
+ puts " Action: #{threat_level > 0.7 ? 'Block immediately, alert SOC' : (threat_level > 0.4 ? 'Increase monitoring' : 'Continue normal operation')}\n\n"
1209
+ end
1210
+ ```
1211
+
1212
+ #### Example 6.6: Loan Default Prediction
1213
+
1214
+ ```ruby
1215
+ require 'grydra'
1216
+
1217
+ # Applicant features: [age, income, credit_score, debt_ratio, employment_years, loan_amount, previous_defaults]
1218
+ applicants = [
1219
+ [35, 75000, 720, 0.3, 8, 25000, 0], # Approved
1220
+ [28, 45000, 650, 0.5, 3, 15000, 1], # Risky
1221
+ [42, 95000, 780, 0.2, 12, 35000, 0], # Approved
1222
+ [25, 35000, 580, 0.7, 1, 10000, 2], # Denied
1223
+ [50, 120000, 800, 0.15, 20, 50000, 0],# Approved
1224
+ [30, 50000, 620, 0.6, 4, 18000, 1], # Risky
1225
+ [38, 85000, 750, 0.25, 10, 30000, 0], # Approved
1226
+ [26, 38000, 590, 0.65, 2, 12000, 2] # Denied
1227
+ ]
1228
+
1229
+ defaults = [[0], [1], [0], [1], [0], [1], [0], [1]]
1230
+
1231
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
1232
+ model.configure_adam_optimizer(alpha: 0.001)
1233
+
1234
+ model.train_numerical(applicants, defaults, [[14, 10, 6, 1]], 0.04, 2500, :zscore,
1235
+ lambda_l2: 0.001, dropout: true, dropout_rate: 0.25,
1236
+ patience: 180)
1237
+
1238
+ # Evaluate model
1239
+ metrics = model.evaluate_model(applicants, defaults, [:accuracy, :confusion_matrix], :zscore)
1240
+
1241
+ puts "🏦 Loan Default Model Performance:"
1242
+ puts " Accuracy: #{(metrics[:accuracy] * 100).round(2)}%"
1243
+ if metrics[:confusion_matrix]
1244
+ cm = metrics[:confusion_matrix]
1245
+ precision = GRYDRA::Metrics.precision(cm[:tp], cm[:fp])
1246
+ recall = GRYDRA::Metrics.recall(cm[:tp], cm[:fn])
1247
+ f1 = GRYDRA::Metrics.f1(precision, recall)
1248
+ puts " Precision: #{(precision * 100).round(2)}%"
1249
+ puts " Recall: #{(recall * 100).round(2)}%"
1250
+ puts " F1 Score: #{(f1 * 100).round(2)}%\n\n"
1251
+ end
1252
+
1253
+ # Evaluate new applications
1254
+ new_applicants = [
1255
+ [33, 68000, 700, 0.35, 6, 22000, 0],
1256
+ [27, 42000, 610, 0.55, 2, 14000, 1]
1257
+ ]
1258
+
1259
+ predictions = model.predict_numerical(new_applicants, :zscore)
1260
+
1261
+ puts "Loan Application Decisions:"
1262
+ new_applicants.each_with_index do |app, i|
1263
+ risk = predictions[i][0]
1264
+ decision = risk < 0.3 ? "✅ APPROVE" : (risk < 0.6 ? "⚠️ REVIEW" : "❌ DENY")
1265
+
1266
+ puts "Applicant #{i + 1}: Age=#{app[0]}, Income=$#{app[1]}, Credit=#{app[2]}"
1267
+ puts " Default Risk: #{(risk * 100).round(1)}%"
1268
+ puts " Decision: #{decision}"
1269
+ puts " Interest Rate: #{risk < 0.3 ? '5.5%' : (risk < 0.6 ? '8.5%' : 'N/A')}\n\n"
1270
+ end
1271
+ ```
1272
+
1273
+ #### Example 6.7: Predictive Maintenance for Manufacturing
1274
+
1275
+ ```ruby
1276
+ require 'grydra'
1277
+
1278
+ # Machine sensor data: [temperature, vibration, pressure, rpm, power_consumption, runtime_hours, last_maintenance_days]
1279
+ machine_data = [
1280
+ [65, 0.5, 100, 1500, 45, 1000, 30], # Healthy
1281
+ [85, 2.5, 95, 1480, 52, 3500, 180], # Needs maintenance
1282
+ [70, 0.8, 98, 1495, 46, 1500, 45], # Healthy
1283
+ [95, 3.5, 88, 1450, 58, 4200, 240], # Critical
1284
+ [68, 0.6, 99, 1498, 45, 1200, 35], # Healthy
1285
+ [80, 2.0, 92, 1475, 50, 3000, 150], # Needs maintenance
1286
+ [72, 1.0, 97, 1490, 47, 1800, 60], # Healthy
1287
+ [90, 3.0, 90, 1460, 55, 3800, 210] # Critical
1288
+ ]
1289
+
1290
+ maintenance_needed = [[0], [1], [0], [1], [0], [1], [0], [1]]
1291
+
1292
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
1293
+ model.configure_adam_optimizer(alpha: 0.001)
1294
+
1295
+ model.train_numerical(machine_data, maintenance_needed, [[14, 10, 6, 1]], 0.04, 2500, :zscore,
1296
+ lambda_l1: 0.001, lambda_l2: 0.01,
1297
+ dropout: true, dropout_rate: 0.3,
1298
+ patience: 180)
1299
+
1300
+ # Monitor machines
1301
+ current_machines = [
1302
+ [73, 1.2, 96, 1488, 48, 2000, 75], # Should be OK
1303
+ [88, 2.8, 91, 1465, 54, 3600, 195], # Should need maintenance
1304
+ [67, 0.7, 99, 1497, 46, 1300, 40] # Should be OK
1305
+ ]
1306
+
1307
+ predictions = model.predict_numerical(current_machines, :zscore)
1308
+
1309
+ puts "🏭 Predictive Maintenance System:"
1310
+ current_machines.each_with_index do |machine, i|
1311
+ failure_risk = predictions[i][0]
1312
+ status = failure_risk > 0.7 ? "🔴 CRITICAL" : (failure_risk > 0.4 ? "🟡 WARNING" : "🟢 HEALTHY")
1313
+
1314
+ puts "Machine #{i + 1}: Temp=#{machine[0]}°C, Vibration=#{machine[1]}mm/s, Runtime=#{machine[5]}hrs"
1315
+ puts " Failure Risk: #{(failure_risk * 100).round(1)}%"
1316
+ puts " Status: #{status}"
1317
+ puts " Recommendation: #{failure_risk > 0.7 ? 'Schedule immediate maintenance' : (failure_risk > 0.4 ? 'Plan maintenance within 2 weeks' : 'Continue normal operation')}"
1318
+ puts " Estimated Time to Failure: #{failure_risk > 0.7 ? '<1 week' : (failure_risk > 0.4 ? '2-4 weeks' : '>1 month')}\n\n"
1319
+ end
1320
+ ```
1321
+
1322
+ #### Example 6.8: Sales Forecasting with Seasonality
1323
+
1324
+ ```ruby
1325
+ require 'grydra'
1326
+
1327
+ # Sales features: [month, day_of_week, is_holiday, temperature, marketing_spend, competitor_promo, inventory_level]
1328
+ historical_sales = [
1329
+ [1, 1, 0, 35, 5000, 0, 1000], # 45k sales
1330
+ [1, 5, 0, 32, 4500, 1, 950], # 38k sales
1331
+ [2, 3, 1, 40, 8000, 0, 1200], # 65k sales (Valentine's)
1332
+ [3, 6, 0, 55, 5500, 0, 1100], # 48k sales
1333
+ [4, 2, 0, 65, 6000, 1, 1050], # 42k sales
1334
+ [5, 7, 1, 75, 7000, 0, 1300], # 58k sales (Memorial Day)
1335
+ [6, 4, 0, 85, 5000, 0, 1000], # 50k sales
1336
+ [7, 1, 1, 90, 9000, 0, 1400] # 72k sales (July 4th)
1337
+ ]
1338
+
1339
+ sales = [[45], [38], [65], [48], [42], [58], [50], [72]]
1340
+
1341
+ model = GRYDRA::Networks::EasyNetwork.new(print_epochs: false)
1342
+ model.configure_adam_optimizer(alpha: 0.001)
1343
+
1344
+ model.train_numerical(historical_sales, sales, [[14, 10, 6, 1]], 0.03, 2500, :zscore,
1345
+ lambda_l2: 0.001, patience: 180)
1346
+
1347
+ # Forecast future sales
1348
+ future_scenarios = [
1349
+ [8, 3, 0, 88, 5500, 0, 1100], # Regular August day
1350
+ [9, 1, 1, 78, 8500, 0, 1350], # Labor Day
1351
+ [10, 5, 0, 65, 6000, 1, 1050] # October with competitor promo
1352
+ ]
1353
+
1354
+ predictions = model.predict_numerical(future_scenarios, :zscore)
1355
+
1356
+ puts "📈 Sales Forecasting:"
1357
+ future_scenarios.each_with_index do |scenario, i|
1358
+ forecast = predictions[i][0]
1359
+ month_names = %w[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec]
1360
+
1361
+ puts "Scenario #{i + 1}: #{month_names[scenario[0] - 1]}, Day #{scenario[1]}, Holiday=#{scenario[2] == 1 ? 'Yes' : 'No'}"
1362
+ puts " Marketing: $#{scenario[4]}, Competitor Promo: #{scenario[5] == 1 ? 'Yes' : 'No'}"
1363
+ puts " Forecasted Sales: $#{forecast.round(1)}k"
1364
+ puts " Confidence: #{forecast > 60 ? 'High season' : (forecast > 45 ? 'Normal' : 'Low season')}"
1365
+ puts " Inventory Recommendation: #{(forecast * 25).round(0)} units\n\n"
1366
+ end
1367
+ ```
1368
+
1369
+ ## Features
1370
+
1371
+ ### Core Components
1372
+ - **7 Activation Functions**: tanh, relu, sigmoid, leaky_relu, swish, gelu, softmax
1373
+ - **8 Loss Functions**: MSE, MAE, Huber, Binary/Categorical Cross-Entropy, Hinge, Log-Cosh, Quantile
1374
+ - **5 Optimizers**: Adam, SGD (with momentum/Nesterov), RMSprop, AdaGrad, AdamW
1375
+ - **3 Layer Types**: Dense (fully connected), Convolutional, LSTM
1376
+ - **6 Training Callbacks**: EarlyStopping, LearningRateScheduler, ReduceLROnPlateau, ModelCheckpoint, CSVLogger, ProgressBar
1377
+
1378
+ ### Advanced Features
1379
+ - **Regularization**: L1 (Lasso), L2 (Ridge), Dropout
1380
+ - **Normalization**: Z-score, Min-Max, Feature-wise
1381
+ - **Model Validation**: K-fold cross-validation, train/test split
1382
+ - **Hyperparameter Tuning**: Grid search with parallel evaluation
1383
+ - **Dimensionality Reduction**: PCA with power iteration
1384
+ - **Text Processing**: Vocabulary creation, TF-IDF vectorization
1385
+ - **Model Persistence**: Save/load models and vocabularies
1386
+ - **Visualization**: ASCII architecture plots, gradient analysis, training curves
1387
+ - **Ensemble Learning**: Multiple subnets with weighted voting
1388
+
1389
+ ### Network Architectures
1390
+ - **EasyNetwork**: High-level API for quick prototyping
1391
+ - **MainNetwork**: Ensemble of multiple subnets
1392
+ - **NeuralNetwork**: Low-level customizable architecture
1393
+ - **Custom Layers**: Build your own layer types
116
1394
 
117
1395
  ## License
118
1396
 
119
- [Show licence](LICENCE)
1397
+ GPL-3.0-or-later
1398
+
1399
+ ## Links
1400
+
1401
+ - GitHub: https://github.com/grcodedigitalsolutions/GRydra
1402
+ - RubyGems: https://rubygems.org/gems/grydra
1403
+
1404
+ ---
1405
+
1406
+ ## 💙 Support Gabo-Razo
1407
+
1408
+ <center>If this project has been useful, consider supporting **Gabo-Razo** via GitHub Sponsors. Your contribution helps keep development active and improve future releases.</center>
1409
+
1410
+ ---
1411
+
1412
+ <div align="center">
1413
+ <img src="https://avatars.githubusercontent.com/u/219750358?v=4" width="90" style="border-radius: 50%; margin-bottom: 12px;"/>
1414
+ <p style="font-size: 22px; font-weight: 800; margin: 0;">Gabo-Razo</p>
1415
+ <p><strong>Developer • Ruby • Python • C++ • Java • Dart • COBOL</strong></p>
1416
+ <p><a href="https://github.com/Gabo-Razo?tab=followers">⭐ Follow on GitHub</a></p>
1417
+ <a href="https://github.com/sponsors/Gabo-Razo"><img src="https://img.shields.io/badge/Sponsor_Me-FF4081?style=for-the-badge&logo=githubsponsors&logoColor=white" height="44"/></a>
1418
+ </div>
1419
+
1420
+ ---