qoa 0.0.3 → 0.0.4
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/README.md +5 -2
- data/lib/qoa/err/validations.rb +27 -0
- data/lib/qoa/layers/convolutional_layer.rb +18 -0
- data/lib/qoa/layers/layer.rb +22 -0
- data/lib/qoa/layers/pooling_layer.rb +13 -0
- data/lib/qoa/loss_functions.rb +30 -0
- data/lib/qoa/matrix_helpers.rb +5 -1
- data/lib/qoa/neural_network.rb +38 -9
- data/lib/qoa/training.rb +100 -7
- data/lib/qoa/utils.rb +1 -7
- data/lib/qoa/version.rb +1 -1
- metadata +21 -3
- data/lib/qoa/layer.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8541feace7b5d8df6418672899fbba8289cc63624d7876c640a28e3143a450f3
|
4
|
+
data.tar.gz: 3944346b90a422a1ea843aef9b0a5bd18c8067e0bc075fa897daab0a1cf9373c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e0afee3076f2d47ff091c2d943c53710e080b015f2773733aabaf90fc4b2ccb26507d24344d1e3b6915e488df875b093577098572c889a1a621064600467037
|
7
|
+
data.tar.gz: aaf6f8e7e9280630fd6218d72a6dd47a4e68602b193a127be8b59462e3204267a28e490e61e8dc19523b8aa463f2d9611fd81617b9da9f79d0e83b1c1942a638
|
data/README.md
CHANGED
@@ -10,6 +10,7 @@ Qoa is a simple and customizable neural network library for Ruby. It allows you
|
|
10
10
|
- Weight initialization using Xavier initialization
|
11
11
|
- Customizable learning parameters
|
12
12
|
- Parallelized backward pass for faster training
|
13
|
+
- Supports convolutional and pooling layers
|
13
14
|
|
14
15
|
## Installation
|
15
16
|
|
@@ -34,7 +35,7 @@ require 'qoa'
|
|
34
35
|
To create a new neural network, you can initialize an instance of `Qoa::NeuralNetwork` with the following parameters:
|
35
36
|
|
36
37
|
- `input_nodes`: The number of input nodes.
|
37
|
-
- `hidden_layers`: An array of the number of nodes in each hidden layer.
|
38
|
+
- `hidden_layers`: An array of the number of nodes in each hidden layer, or an array of `[:conv, nodes, kernel_size, stride]` for a convolutional layer or `[:pool, nodes, pool_size, stride]` for a pooling layer.
|
38
39
|
- `output_nodes`: The number of output nodes.
|
39
40
|
- `learning_rate`: The learning rate for the gradient descent optimization.
|
40
41
|
- `dropout_rate`: The dropout rate for regularization.
|
@@ -42,6 +43,8 @@ To create a new neural network, you can initialize an instance of `Qoa::NeuralNe
|
|
42
43
|
- `decay_rate`: The decay rate for the RMSProp optimizer (default is `0.9`).
|
43
44
|
- `epsilon`: A small value to prevent division by zero in the RMSProp optimizer (default is `1e-8`).
|
44
45
|
- `batch_size`: The size of the mini-batches used for training (default is `10`).
|
46
|
+
- `l1_lambda`: The L1 regularization parameter (default is `0.0`).
|
47
|
+
- `l2_lambda`: The L2 regularization parameter (default is `0.0`).
|
45
48
|
|
46
49
|
Example:
|
47
50
|
|
@@ -49,7 +52,7 @@ Example:
|
|
49
52
|
require 'qoa'
|
50
53
|
|
51
54
|
input_nodes = 784 # Number of input features (e.g., 28x28 pixels for MNIST dataset)
|
52
|
-
hidden_layers = [128, 64] #
|
55
|
+
hidden_layers = [128, [:conv, 64, 3, 1]] # One hidden layer with 128 nodes and one convolutional layer with 64 nodes, kernel size 3, and stride 1
|
53
56
|
output_nodes = 10 # Number of output classes (e.g., 10 for MNIST dataset)
|
54
57
|
learning_rate = 0.01
|
55
58
|
dropout_rate = 0.5
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Qoa
|
2
|
+
module Err
|
3
|
+
module Validations
|
4
|
+
def validate_constructor_args(input_nodes, hidden_layers, output_nodes, learning_rate, dropout_rate, activation_func, decay_rate, epsilon, batch_size, l1_lambda, l2_lambda)
|
5
|
+
raise ArgumentError, 'input_nodes, hidden_layers, and output_nodes must be positive integers' unless [input_nodes, output_nodes].all? { |x| x.is_a?(Integer) && x > 0 } && hidden_layers.is_a?(Array) && hidden_layers.all? { |x| x.is_a?(Integer) && x > 0 }
|
6
|
+
raise ArgumentError, 'learning_rate, dropout_rate, decay_rate, epsilon, l1_lambda, and l2_lambda must be positive numbers' unless [learning_rate, dropout_rate, decay_rate, epsilon, l1_lambda, l2_lambda].all? { |x| x.is_a?(Numeric) && x >= 0 }
|
7
|
+
raise ArgumentError, 'activation_func must be a valid symbol' unless ActivationFunctions.methods.include?(activation_func)
|
8
|
+
raise ArgumentError, 'batch_size must be a positive integer' unless batch_size.is_a?(Integer) && batch_size > 0
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate_query_args(inputs)
|
12
|
+
raise ArgumentError, 'inputs must be an array of numbers' unless inputs.is_a?(Array) && inputs.all? { |x| x.is_a?(Numeric) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate_calculate_loss_args(inputs, targets, loss_function)
|
16
|
+
raise ArgumentError, 'inputs and targets must have the same length' if inputs.size != targets.size
|
17
|
+
raise ArgumentError, 'inputs and targets must be arrays of arrays of numbers' unless inputs.is_a?(Array) && targets.is_a?(Array) && inputs.all? { |x| x.is_a?(Array) && x.all? { |y| y.is_a?(Numeric) } } && targets.all? { |x| x.is_a?(Array) && x.all? { |y| y.is_a?(Numeric) } }
|
18
|
+
raise ArgumentError, 'loss_function must be a valid symbol' unless LossFunctions.methods.include?(loss_function)
|
19
|
+
end
|
20
|
+
|
21
|
+
def validate_train_args(inputs, targets)
|
22
|
+
raise ArgumentError, 'inputs and targets must have the same length' if inputs.size != targets.size
|
23
|
+
raise ArgumentError, 'inputs and targets must be arrays of arrays of numbers' unless inputs.is_a?(Array) && targets.is_a?(Array) && inputs.all? { |x| x.is_a?(Array) && x.all? { |y| y.is_a?(Numeric) } } && targets.all? { |x| x.is_a?(Array) && x.all? { |y| y.is_a?(Numeric) } }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Qoa
|
2
|
+
module Layers
|
3
|
+
class ConvolutionalLayer < Qoa::Layers::Layer
|
4
|
+
attr_reader :kernel_size, :stride
|
5
|
+
|
6
|
+
def initialize(input_size, output_size, kernel_size, stride = 1)
|
7
|
+
super(input_size, output_size)
|
8
|
+
@kernel_size = kernel_size
|
9
|
+
@stride = stride
|
10
|
+
end
|
11
|
+
|
12
|
+
def random_matrix(rows, cols)
|
13
|
+
limit = Math.sqrt(6.0 / (rows + cols))
|
14
|
+
Array.new(rows) { Array.new(cols) { rand(-limit..limit) } }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Qoa
|
2
|
+
module Layers
|
3
|
+
class Layer
|
4
|
+
attr_reader :input_size, :output_size, :weights
|
5
|
+
|
6
|
+
def initialize(input_size, output_size)
|
7
|
+
@input_size = input_size
|
8
|
+
@output_size = output_size
|
9
|
+
@weights = random_matrix(output_size, input_size)
|
10
|
+
end
|
11
|
+
|
12
|
+
def random_matrix(rows, cols)
|
13
|
+
limit = Math.sqrt(6.0 / (rows + cols))
|
14
|
+
Array.new(rows) { Array.new(cols) { rand(-limit..limit) } }
|
15
|
+
end
|
16
|
+
|
17
|
+
def weights=(new_weights)
|
18
|
+
@weights = new_weights
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Qoa
|
2
|
+
module Layers
|
3
|
+
class PoolingLayer < Qoa::Layers::Layer
|
4
|
+
attr_reader :pool_size, :stride
|
5
|
+
|
6
|
+
def initialize(input_size, output_size, pool_size, stride = 1)
|
7
|
+
super(input_size, output_size)
|
8
|
+
@pool_size = pool_size
|
9
|
+
@stride = stride
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Qoa
|
2
|
+
module LossFunctions
|
3
|
+
class << self
|
4
|
+
def mean_squared_error(prediction, target)
|
5
|
+
raise ArgumentError, 'prediction and target must have the same length' if prediction.size != target.size
|
6
|
+
prediction.zip(target).map { |p, t| (p - t) ** 2 }.sum / prediction.size
|
7
|
+
end
|
8
|
+
|
9
|
+
def cross_entropy_loss(prediction, target)
|
10
|
+
raise ArgumentError, 'prediction and target must have the same length' if prediction.size != target.size
|
11
|
+
-prediction.zip(target).map { |p, t| t * Math.log(p) }.sum / prediction.size
|
12
|
+
end
|
13
|
+
|
14
|
+
def binary_cross_entropy(prediction, target)
|
15
|
+
raise ArgumentError, 'prediction and target must have the same length' if prediction.size != target.size
|
16
|
+
-prediction.zip(target).map { |p, t| t * Math.log(p) + (1 - t) * Math.log(1 - p) }.sum / prediction.size
|
17
|
+
end
|
18
|
+
|
19
|
+
def categorical_cross_entropy(prediction, target)
|
20
|
+
raise ArgumentError, 'prediction and target must have the same length' if prediction.size != target.size
|
21
|
+
-prediction.zip(target).map { |p, t| t * Math.log(p) }.sum / prediction.size
|
22
|
+
end
|
23
|
+
|
24
|
+
def mean_absolute_error(prediction, target)
|
25
|
+
raise ArgumentError, 'prediction and target must have the same length' if prediction.size != target.size
|
26
|
+
prediction.zip(target).map { |p, t| (p - t).abs }.sum / prediction.size
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/qoa/matrix_helpers.rb
CHANGED
@@ -24,7 +24,11 @@ module Qoa
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def apply_function(matrix, func)
|
27
|
-
matrix.map
|
27
|
+
matrix.map do |row|
|
28
|
+
row.map do |x|
|
29
|
+
x.nil? ? nil : func.call(x) # Add a check for nil values
|
30
|
+
end
|
31
|
+
end
|
28
32
|
end
|
29
33
|
|
30
34
|
def transpose(matrix)
|
data/lib/qoa/neural_network.rb
CHANGED
@@ -1,15 +1,24 @@
|
|
1
|
-
require_relative 'layer'
|
1
|
+
require_relative 'layers/layer'
|
2
|
+
require_relative 'layers/convolutional_layer'
|
3
|
+
require_relative 'layers/pooling_layer'
|
2
4
|
require_relative 'activation_functions'
|
3
5
|
require_relative 'training'
|
4
6
|
require_relative 'utils'
|
7
|
+
require_relative 'loss_functions'
|
8
|
+
require_relative 'err/validations'
|
5
9
|
|
6
10
|
module Qoa
|
7
11
|
class NeuralNetwork
|
8
12
|
include Training
|
9
13
|
include Utils
|
10
|
-
|
14
|
+
include LossFunctions
|
15
|
+
include Err::Validations
|
16
|
+
|
17
|
+
attr_reader :input_nodes, :hidden_layers, :output_nodes, :learning_rate, :activation_func, :dropout_rate, :decay_rate, :epsilon, :batch_size, :l1_lambda, :l2_lambda
|
18
|
+
|
19
|
+
def initialize(input_nodes, hidden_layers, output_nodes, learning_rate, dropout_rate, activation_func = :leaky_relu, decay_rate = 0.9, epsilon = 1e-8, batch_size = 10, l1_lambda = 0.0, l2_lambda = 0.0)
|
20
|
+
# validate_constructor_args(input_nodes, hidden_layers, output_nodes, learning_rate, dropout_rate, activation_func, decay_rate, epsilon, batch_size, l1_lambda, l2_lambda)
|
11
21
|
|
12
|
-
def initialize(input_nodes, hidden_layers, output_nodes, learning_rate, dropout_rate, activation_func = :sigmoid, decay_rate = 0.9, epsilon = 1e-8, batch_size = 10)
|
13
22
|
@input_nodes = input_nodes
|
14
23
|
@hidden_layers = hidden_layers
|
15
24
|
@output_nodes = output_nodes
|
@@ -19,27 +28,47 @@ module Qoa
|
|
19
28
|
@decay_rate = decay_rate
|
20
29
|
@epsilon = epsilon
|
21
30
|
@batch_size = batch_size
|
31
|
+
@l1_lambda = l1_lambda
|
32
|
+
@l2_lambda = l2_lambda
|
22
33
|
|
23
34
|
@layers = []
|
24
|
-
@layers << Layer.new(input_nodes, hidden_layers[0])
|
35
|
+
@layers << Qoa::Layers::Layer.new(input_nodes, hidden_layers[0].is_a?(Array) ? hidden_layers[0][1] : hidden_layers[0])
|
36
|
+
|
25
37
|
hidden_layers.each_cons(2) do |l1, l2|
|
26
|
-
|
38
|
+
l1_size = l1.is_a?(Array) ? l1[1] : l1
|
39
|
+
l2_size = l2.is_a?(Array) ? l2[1] : l2
|
40
|
+
|
41
|
+
if l1.is_a?(Array) && l2.is_a?(Array) && l1[0] == :conv && l2[0] == :conv
|
42
|
+
@layers << Qoa::Layers::ConvolutionalLayer.new(l1_size, l2_size, l1[2], l1[3])
|
43
|
+
elsif l1.is_a?(Array) && l1[0] == :conv && l2.is_a?(Numeric)
|
44
|
+
@layers << Qoa::Layers::ConvolutionalLayer.new(l1_size, l2_size, l1[2], l1[3])
|
45
|
+
elsif l1.is_a?(Numeric) && l2.is_a?(Array) && l2[0] == :conv
|
46
|
+
@layers << Qoa::Layers::ConvolutionalLayer.new(l1_size, l2_size, l2[2], l2[3])
|
47
|
+
elsif l1.is_a?(Array) && l1[0] == :pool && l2.is_a?(Numeric)
|
48
|
+
@layers << Qoa::Layers::PoolingLayer.new(l1_size, l2_size, l1[2], l1[3])
|
49
|
+
elsif l1.is_a?(Numeric) && l2.is_a?(Array) && l2[0] == :pool
|
50
|
+
@layers << Qoa::Layers::PoolingLayer.new(l1_size, l2_size, l2[2], l2[3])
|
51
|
+
else
|
52
|
+
@layers << Qoa::Layers::Layer.new(l1_size, l2_size)
|
53
|
+
end
|
27
54
|
end
|
28
|
-
@layers << Layer.new(hidden_layers[-1], output_nodes)
|
55
|
+
@layers << Qoa::Layers::Layer.new(hidden_layers[-1].is_a?(Array) ? hidden_layers[-1][1] : hidden_layers[-1], output_nodes)
|
29
56
|
end
|
30
57
|
|
31
58
|
def query(inputs)
|
59
|
+
validate_query_args(inputs)
|
60
|
+
|
32
61
|
layer_outputs = forward_pass(inputs)
|
33
62
|
layer_outputs.last.flatten
|
34
63
|
end
|
35
64
|
|
36
|
-
def calculate_loss(inputs, targets)
|
37
|
-
|
65
|
+
def calculate_loss(inputs, targets, loss_function = :cross_entropy_loss)
|
66
|
+
validate_calculate_loss_args(inputs, targets, loss_function)
|
38
67
|
|
39
68
|
total_loss = 0.0
|
40
69
|
inputs.zip(targets).each do |input, target|
|
41
70
|
prediction = query(input)
|
42
|
-
total_loss +=
|
71
|
+
total_loss += LossFunctions.send(loss_function, prediction, target)
|
43
72
|
end
|
44
73
|
|
45
74
|
total_loss / inputs.size
|
data/lib/qoa/training.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
-
require_relative 'matrix_helpers'
|
2
1
|
require 'concurrent'
|
2
|
+
require_relative 'matrix_helpers'
|
3
|
+
require_relative 'err/validations'
|
3
4
|
|
4
5
|
module Qoa
|
5
6
|
module Training
|
6
7
|
include MatrixHelpers
|
8
|
+
include Err::Validations
|
7
9
|
|
8
10
|
def train(inputs, targets)
|
9
|
-
|
11
|
+
validate_train_args(inputs, targets)
|
10
12
|
|
11
13
|
inputs.zip(targets).each_slice(@batch_size) do |batch|
|
12
14
|
train_batch(batch)
|
@@ -44,7 +46,8 @@ module Qoa
|
|
44
46
|
|
45
47
|
# Update weights
|
46
48
|
@layers.each_with_index do |layer, i|
|
47
|
-
|
49
|
+
regularization_penalty = calculate_regularization_penalty(layer.weights, @l1_lambda, @l2_lambda)
|
50
|
+
layer.weights = matrix_add(layer.weights, scalar_multiply(@learning_rate / batch.size, matrix_add(weight_deltas[i], regularization_penalty)))
|
48
51
|
end
|
49
52
|
end
|
50
53
|
|
@@ -77,8 +80,15 @@ module Qoa
|
|
77
80
|
inputs = inputs.map { |x| [x] } # Convert to column vector
|
78
81
|
|
79
82
|
layer_outputs = [inputs]
|
80
|
-
@layers.
|
81
|
-
|
83
|
+
@layers.each_with_index do |layer, i|
|
84
|
+
if layer.is_a?(Qoa::Layers::ConvolutionalLayer)
|
85
|
+
layer_inputs = convolution(layer, layer_outputs[-1])
|
86
|
+
elsif layer.is_a?(Qoa::Layers::PoolingLayer)
|
87
|
+
layer_inputs = pooling(layer, layer_outputs[-1])
|
88
|
+
else
|
89
|
+
layer_inputs = matrix_multiply(layer.weights, layer_outputs[-1])
|
90
|
+
end
|
91
|
+
|
82
92
|
layer_outputs << apply_function(layer_inputs, ActivationFunctions.method(@activation_func))
|
83
93
|
|
84
94
|
# Apply dropout to hidden layers
|
@@ -101,13 +111,96 @@ module Qoa
|
|
101
111
|
|
102
112
|
# Compute weight deltas
|
103
113
|
weight_deltas = []
|
104
|
-
@layers.each_with_index do |
|
114
|
+
@layers.each_with_index do |layer, i|
|
105
115
|
gradients = matrix_multiply_element_wise(errors[i], apply_function(layer_outputs[i + 1], ActivationFunctions.method(derivative_func)))
|
106
|
-
|
116
|
+
if layer.is_a?(Qoa::Layers::ConvolutionalLayer)
|
117
|
+
w_delta = conv_weight_delta(layer, gradients, layer_outputs[i])
|
118
|
+
elsif layer.is_a?(Qoa::Layers::PoolingLayer)
|
119
|
+
w_delta = pool_weight_delta(layer, gradients, layer_outputs[i])
|
120
|
+
else
|
121
|
+
w_delta = matrix_multiply(gradients, transpose(layer_outputs[i]))
|
122
|
+
end
|
107
123
|
weight_deltas << w_delta
|
108
124
|
end
|
109
125
|
|
110
126
|
weight_deltas
|
111
127
|
end
|
128
|
+
|
129
|
+
def calculate_regularization_penalty(weights, l1_lambda, l2_lambda)
|
130
|
+
l1_penalty = weights.map do |row|
|
131
|
+
row.nil? ? nil : row.map { |x| x.nil? ? nil : (x < 0 ? -1 : 1) }
|
132
|
+
end
|
133
|
+
l1_penalty = scalar_multiply(l1_lambda, l1_penalty)
|
134
|
+
|
135
|
+
l2_penalty = scalar_multiply(l2_lambda, weights)
|
136
|
+
|
137
|
+
matrix_add(l1_penalty, l2_penalty)
|
138
|
+
end
|
139
|
+
|
140
|
+
def convolution(layer, inputs)
|
141
|
+
output_size = layer.output_size
|
142
|
+
kernel_size = layer.kernel_size
|
143
|
+
stride = layer.stride
|
144
|
+
|
145
|
+
output = Array.new(output_size) { Array.new(inputs.length - kernel_size + 1) }
|
146
|
+
layer.weights.each_with_index do |row, i|
|
147
|
+
inputs.each_cons(kernel_size).each_with_index do |input_slice, j|
|
148
|
+
output[i][j] = row.zip(input_slice).map { |a, b| a * b }.reduce(:+)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
output
|
153
|
+
end
|
154
|
+
|
155
|
+
def pooling(layer, inputs)
|
156
|
+
output_size = layer.output_size
|
157
|
+
pool_size = layer.pool_size
|
158
|
+
stride = layer.stride || 1
|
159
|
+
|
160
|
+
# Calculate the number of columns in the output array
|
161
|
+
output_columns = inputs[0].length - pool_size
|
162
|
+
output_columns = output_columns <= 0 ? 1 : ((output_columns) / stride.to_f).ceil + 1
|
163
|
+
|
164
|
+
output = Array.new(output_size) { Array.new(output_columns) }
|
165
|
+
|
166
|
+
(0...output_size).each do |i|
|
167
|
+
(0...output_columns).each do |j|
|
168
|
+
start_idx = j * stride
|
169
|
+
end_idx = start_idx + pool_size - 1
|
170
|
+
next if inputs[i].nil? || inputs[i].length < end_idx + 1 # Add this check to avoid accessing an index that does not exist
|
171
|
+
pool_slice = inputs[i].slice(start_idx..end_idx)
|
172
|
+
output[i][j] = pool_slice.max
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
output
|
177
|
+
end
|
178
|
+
|
179
|
+
def conv_weight_delta(layer, gradients, inputs)
|
180
|
+
kernel_size = layer.kernel_size
|
181
|
+
stride = layer.stride
|
182
|
+
|
183
|
+
deltas = layer.weights.map do |row|
|
184
|
+
inputs.each_cons(kernel_size).map do |input_slice|
|
185
|
+
row.zip(input_slice).map { |a, b| a * b }.reduce(:+)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
deltas
|
190
|
+
end
|
191
|
+
|
192
|
+
def pool_weight_delta(layer, gradients, inputs)
|
193
|
+
pool_size = layer.pool_size
|
194
|
+
stride = layer.stride
|
195
|
+
|
196
|
+
deltas = inputs.each_slice(stride).map do |input_slice|
|
197
|
+
input_slice.each_cons(pool_size).map do |pool_slice|
|
198
|
+
max_index = pool_slice.each_with_index.max[1]
|
199
|
+
pool_slice.map.with_index { |v, i| (i == max_index) ? v * gradients[i] : 0 }
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
deltas
|
204
|
+
end
|
112
205
|
end
|
113
206
|
end
|
data/lib/qoa/utils.rb
CHANGED
@@ -34,16 +34,10 @@ module Qoa
|
|
34
34
|
@epsilon = model_data[:epsilon]
|
35
35
|
@batch_size = model_data[:batch_size]
|
36
36
|
|
37
|
-
@layers = model_data[:weights].map { |w| Layer.new(w.first.size, w.size) }
|
37
|
+
@layers = model_data[:weights].map { |w| Qoa::Layers::Layer.new(w.first.size, w.size) }
|
38
38
|
@layers.each_with_index do |layer, i|
|
39
39
|
layer.weights = model_data[:weights][i]
|
40
40
|
end
|
41
41
|
end
|
42
|
-
|
43
|
-
def mean_squared_error(prediction, target)
|
44
|
-
raise ArgumentError, 'prediction and target must have the same length' if prediction.size != target.size
|
45
|
-
|
46
|
-
prediction.zip(target).map { |p, t| (p - t) ** 2 }.sum / prediction.size
|
47
|
-
end
|
48
42
|
end
|
49
43
|
end
|
data/lib/qoa/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qoa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel M. Matongo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: concurrent-ruby
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.1'
|
55
69
|
description: Qoa is a simple machine learning library for Ruby, including a basic
|
56
70
|
feedforward neural network implementation with backpropagation.
|
57
71
|
email:
|
@@ -65,7 +79,11 @@ files:
|
|
65
79
|
- code_of_conduct.md
|
66
80
|
- lib/qoa.rb
|
67
81
|
- lib/qoa/activation_functions.rb
|
68
|
-
- lib/qoa/
|
82
|
+
- lib/qoa/err/validations.rb
|
83
|
+
- lib/qoa/layers/convolutional_layer.rb
|
84
|
+
- lib/qoa/layers/layer.rb
|
85
|
+
- lib/qoa/layers/pooling_layer.rb
|
86
|
+
- lib/qoa/loss_functions.rb
|
69
87
|
- lib/qoa/matrix_helpers.rb
|
70
88
|
- lib/qoa/neural_network.rb
|
71
89
|
- lib/qoa/training.rb
|
data/lib/qoa/layer.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
module Qoa
|
2
|
-
class Layer
|
3
|
-
attr_reader :input_size, :output_size, :weights
|
4
|
-
|
5
|
-
def initialize(input_size, output_size)
|
6
|
-
@input_size = input_size
|
7
|
-
@output_size = output_size
|
8
|
-
@weights = random_matrix(output_size, input_size)
|
9
|
-
end
|
10
|
-
|
11
|
-
def random_matrix(rows, cols)
|
12
|
-
limit = Math.sqrt(6.0 / (rows + cols))
|
13
|
-
Array.new(rows) { Array.new(cols) { rand(-limit..limit) } }
|
14
|
-
end
|
15
|
-
|
16
|
-
def weights=(new_weights)
|
17
|
-
@weights = new_weights
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|