-
Notifications
You must be signed in to change notification settings - Fork 1
/
neural_network.rb
84 lines (73 loc) · 2.42 KB
/
neural_network.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
require 'matrix'
require 'nmatrix'
require 'pp'
class NeuralNetwork
attr_reader :threshold
def self.build(inputs, outputs, hidden_layer_size, threshold)
nn = NeuralNetwork.new(inputs, outputs, hidden_layer_size, threshold)
iteration_count = 0
loop do
error = nn.determine_error(iteration_count)
break if error.to_a.all? { |x| x < threshold }
nn.train(inputs, outputs)
iteration_count += 1
end
nn
end
def determine_error(iteration_count)
if iteration_count % 100 == 0
pp "Iteration: #{iteration_count}"
puts "Results"
pp @outputs
puts "\nPredicted Result"
pp ff(@inputs)
end
error = ((@outputs - ff(@inputs)) ** 2).mean
if iteration_count % 100 == 0
pp "Error: #{error}"
puts "\n\n\n"
end
error
end
def initialize(inputs, outputs, hidden_size, threshold)
@inputs = inputs
@outputs = outputs
@hiddenSize = hidden_size
@threshold = threshold
@inputSize = @inputs.row(0).size
@outputSize = @outputs.row(0).size
# Produce weight matrices of same size as hidden / ouput layers
@weights = {
hidden: NMatrix.new([@inputSize, @hiddenSize], random_weights(@inputs)),
output: NMatrix.new([@hiddenSize, @outputSize], random_weights(@outputs))
}
end
# Produce random weights for all cells matrix argument
def random_weights(source)
(1..source.size).to_a.map { rand }
end
# Apply sigmoid activation function to dot product of weights matrix
# Apply dot producr of previous layers output to output weights
# Apply sigmoid activation to output dot product
def ff(input)
dot_product = input.dot(@weights[:hidden])
@sigmoid_product = sigmoid(dot_product)
dot_product_of_activation = @sigmoid_product.dot(@weights[:output])
sigmoid(dot_product_of_activation)
end
def sigmoid(matrix)
matrix.map { |cell| 1.0 / (1.0 + Math::E ** -cell) }
end
def sigmoid_compliment(matrix)
matrix * matrix.map { |cell| 1.0 - cell }
end
def fb(input, output, ff_result)
output_delta = (output - ff_result) * sigmoid_compliment(ff_result)
sigmoid_compliment_delta = output_delta.dot(@weights[:output].transpose) * sigmoid_compliment(@sigmoid_product)
@weights[:hidden] += input.transpose.dot(sigmoid_compliment_delta)
@weights[:output] += @sigmoid_product.transpose.dot(output_delta)
end
def train(input, output)
fb(input, output, ff(input))
end
end