rann 0.2.7 → 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGES.md +43 -0
- data/Gemfile.lock +1 -1
- data/examples/step_by_step.rb +57 -0
- data/lib/rann.rb +3 -0
- data/lib/rann/backprop.rb +53 -36
- data/lib/rann/connection.rb +3 -2
- data/lib/rann/gradient_checker.rb +7 -5
- data/lib/rann/lstm.rb +24 -30
- data/lib/rann/network.rb +18 -4
- data/lib/rann/neuron.rb +4 -3
- data/lib/rann/optimisers/adagrad.rb +2 -2
- data/lib/rann/optimisers/rmsprop.rb +2 -2
- data/lib/rann/product_neuron.rb +1 -1
- data/lib/rann/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2cd393e231a8a1da7a732eff0ea1e82008cc5dcc
|
4
|
+
data.tar.gz: 479d4669daab9e8f2fd60b06044d754e78479ad9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55e2b084f9477543cdc8bbb3cd415e80bfd6c3f75517b61cd1969e9070aa2045c27a6003655394414414c11155cbf042aa597d8ef4798bdf4a2ffcfedd02782e
|
7
|
+
data.tar.gz: c2ca6ed22034d63d2a256d7a898b8c5c3c12fcf5674d133e34e405357740ca11ba2002ad26c5506e56b861349db42c8ede21bbc22ba57957bc447b515d4bdef1
|
data/CHANGES.md
CHANGED
@@ -1,3 +1,46 @@
|
|
1
|
+
- More backprop fixes..
|
2
|
+
|
3
|
+
*Michael Campbell*
|
4
|
+
|
5
|
+
- Give LSTM input neurons linear activation.
|
6
|
+
|
7
|
+
*Michael Campbell*
|
8
|
+
|
9
|
+
- For context neurons connected to a product neuron, it's initial value should
|
10
|
+
be 1.
|
11
|
+
|
12
|
+
*Michael Campbell*
|
13
|
+
|
14
|
+
- So. Many. Bugs. Turns out the product neuron stuff was still broken and
|
15
|
+
network evaluation wasn't behaving correctly with context neurons (recurrent
|
16
|
+
connections). Also an error in timestep handling during backprop, and just
|
17
|
+
generally network traversal ...
|
18
|
+
|
19
|
+
*Michael Campbell*
|
20
|
+
|
21
|
+
- Don't lock the input connections into the LSTM layer, that acts as the fully
|
22
|
+
connected part of the network and that's where the majority of learning takes
|
23
|
+
place, derp.
|
24
|
+
|
25
|
+
*Michael Campbell*
|
26
|
+
|
27
|
+
- Don't change the weight of a LockedConnection during initialisation. (eugh)
|
28
|
+
|
29
|
+
*Michael Campbell*
|
30
|
+
|
31
|
+
- Fix bug in backprop over product neuron.
|
32
|
+
|
33
|
+
*Michael Campbell*
|
34
|
+
|
35
|
+
- Worked out how to use BigDecimal properly, lol. Refactored to use
|
36
|
+
`BigDecimal.limit`.
|
37
|
+
|
38
|
+
*Michael Campbell*
|
39
|
+
|
40
|
+
- Allow customisable precision, defaults to 10.
|
41
|
+
|
42
|
+
*Michael Campbell*
|
43
|
+
|
1
44
|
## 0.2.7 (December 2, 2017)
|
2
45
|
|
3
46
|
- Allow different weights to the different gates in the LSTM. Previously it was
|
data/Gemfile.lock
CHANGED
@@ -0,0 +1,57 @@
|
|
1
|
+
# A reproduction of the wonderful step by step backprop example at
|
2
|
+
# https://mattmazur.com/2015/03/17/a-step-by-step-backpropagation-example/
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "rann"
|
6
|
+
|
7
|
+
# inputs
|
8
|
+
inputs = Array.new(2){ |i| RANN::Neuron.new "input #{i}", 0, :input, :sig }
|
9
|
+
|
10
|
+
# hidden layer
|
11
|
+
hiddens = Array.new(2){ |i| RANN::Neuron.new "hidden #{i}", 3, :standard, :sig }
|
12
|
+
hidden_bias = RANN::Neuron.new "bias", 0, :bias
|
13
|
+
|
14
|
+
# output layer
|
15
|
+
outputs = Array.new(2){ |i| RANN::Neuron.new "output #{i}", 3, :output, :sig }
|
16
|
+
output_bias = RANN::Neuron.new "bias", 0, :bias
|
17
|
+
|
18
|
+
# connect it all w/initial weights
|
19
|
+
connections = []
|
20
|
+
connections << RANN::Connection.new(inputs[0], hiddens[0], 0.15.to_d)
|
21
|
+
connections << RANN::Connection.new(inputs[0], hiddens[1], 0.25.to_d)
|
22
|
+
connections << RANN::Connection.new(inputs[1], hiddens[0], 0.2.to_d)
|
23
|
+
connections << RANN::Connection.new(inputs[1], hiddens[1], 0.3.to_d)
|
24
|
+
connections << RANN::Connection.new(hidden_bias, hiddens[0], 0.35.to_d)
|
25
|
+
connections << RANN::Connection.new(hidden_bias, hiddens[1], 0.35.to_d)
|
26
|
+
|
27
|
+
connections << RANN::Connection.new(hiddens[0], outputs[0], 0.4.to_d)
|
28
|
+
connections << RANN::Connection.new(hiddens[0], outputs[1], 0.5.to_d)
|
29
|
+
connections << RANN::Connection.new(hiddens[1], outputs[0], 0.45.to_d)
|
30
|
+
connections << RANN::Connection.new(hiddens[1], outputs[1], 0.55.to_d)
|
31
|
+
connections << RANN::Connection.new(output_bias, outputs[0], 0.6.to_d)
|
32
|
+
connections << RANN::Connection.new(output_bias, outputs[1], 0.6.to_d)
|
33
|
+
|
34
|
+
network = RANN::Network.new connections
|
35
|
+
backprop = RANN::Backprop.new network
|
36
|
+
|
37
|
+
inputs = [0.05.to_d, 0.10.to_d]
|
38
|
+
targets = [0.1.to_d, 0.99.to_d]
|
39
|
+
outputs = network.evaluate [0.05.to_d, 0.10.to_d]
|
40
|
+
|
41
|
+
puts "forward prop outputs: #{outputs.map(&:to_f).inspect}"
|
42
|
+
|
43
|
+
network.reset!
|
44
|
+
|
45
|
+
gradients, error =
|
46
|
+
RANN::Backprop.run_single(
|
47
|
+
network,
|
48
|
+
[0.05.to_d, 0.10.to_d],
|
49
|
+
[0.01.to_d, 0.99.to_d]
|
50
|
+
)
|
51
|
+
|
52
|
+
puts "error: #{error.to_f}"
|
53
|
+
puts "backprop gradients & updates:"
|
54
|
+
gradients.each do |cid, g|
|
55
|
+
c = network.connections.find{ |c| c.id == cid }
|
56
|
+
puts "#{c.input_neuron.name} -> #{c.output_neuron.name}: g = #{g.to_f}, u = #{(c.weight - 0.5.to_d * g).to_f}"
|
57
|
+
end
|
data/lib/rann.rb
CHANGED
data/lib/rann/backprop.rb
CHANGED
@@ -12,9 +12,9 @@ module RANN
|
|
12
12
|
|
13
13
|
ACTIVATION_DERIVATIVES = {
|
14
14
|
relu: ->(x){ x > 0 ? 1.to_d : 0.to_d },
|
15
|
-
sig: ->(x){ x
|
15
|
+
sig: ->(x){ x * (1 - x) },
|
16
16
|
linear: ->(_){ 1.to_d },
|
17
|
-
tanh: ->(x){ 1 - x
|
17
|
+
tanh: ->(x){ 1 - x ** 2 },
|
18
18
|
step: ->(_){ 0.to_d },
|
19
19
|
}
|
20
20
|
|
@@ -60,9 +60,9 @@ module RANN
|
|
60
60
|
gradients, error = Backprop.run_single network, input, targets[i + j]
|
61
61
|
|
62
62
|
gradients.each do |cid, g|
|
63
|
-
group_avg_gradients[cid] += g
|
63
|
+
group_avg_gradients[cid] += g / batch_size
|
64
64
|
end
|
65
|
-
group_avg_error += error
|
65
|
+
group_avg_error += error / batch_size
|
66
66
|
end
|
67
67
|
|
68
68
|
group_avg_gradients.default_proc = nil
|
@@ -113,64 +113,80 @@ module RANN
|
|
113
113
|
error = mse targets, outputs
|
114
114
|
|
115
115
|
# backward pass with unravelling for recurrent networks
|
116
|
-
node_deltas = Hash.new{ |h, k| h[k] =
|
117
|
-
gradients = Hash.new 0.to_d
|
118
|
-
|
116
|
+
node_deltas = Hash.new{ |h, k| h[k] = {} }
|
119
117
|
initial_timestep = inputs.size - 1
|
120
118
|
neuron_stack = network.output_neurons.map{ |n| [n, initial_timestep] }
|
119
|
+
# initialize network end-point node_deltas in all timesteps with zero
|
120
|
+
network.neurons_with_no_outgoing_connections.each do |n|
|
121
|
+
(0...(inputs.size - 1)).each do |i|
|
122
|
+
node_deltas[i][n.id] = 0.to_d
|
123
|
+
neuron_stack << [n, i]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
gradients = Hash.new 0.to_d
|
121
127
|
|
122
128
|
while current = neuron_stack.shift
|
123
129
|
neuron, timestep = current
|
124
130
|
next if node_deltas[timestep].key? neuron.id
|
125
131
|
|
126
|
-
from_here = bptt_connecting_to neuron, network, timestep
|
127
|
-
neuron_stack.push *from_here
|
128
|
-
|
129
132
|
# neuron delta is summation of neuron deltas deltas for the connections
|
130
133
|
# from this neuron
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
134
|
+
if neuron.output?
|
135
|
+
output_index = network.output_neurons.index neuron
|
136
|
+
step_one = mse_delta targets[output_index], outputs[output_index]
|
137
|
+
else
|
138
|
+
sum =
|
136
139
|
network.connections_from(neuron).reduce 0.to_d do |m, c|
|
137
140
|
out_timestep = c.output_neuron.context? ? timestep + 1 : timestep
|
138
141
|
output_node_delta = node_deltas[out_timestep][c.output_neuron.id]
|
139
142
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
143
|
+
if out_timestep > initial_timestep
|
144
|
+
m
|
145
|
+
elsif !output_node_delta
|
146
|
+
break
|
147
|
+
else
|
148
|
+
# connection delta is the output neuron delta multiplied by the
|
149
|
+
# connection's weight
|
150
|
+
connection_delta =
|
151
|
+
if c.output_neuron.is_a? ProductNeuron
|
152
|
+
intermediate =
|
153
|
+
network.connections_to(c.output_neuron).reject{ |c2| c2 == c }.reduce 1.to_d do |m, c2|
|
154
|
+
m * states[timestep][:values][c2.input_neuron.id] * c2.weight
|
155
|
+
end
|
156
|
+
output_node_delta * intermediate * c.weight
|
157
|
+
else
|
158
|
+
output_node_delta * c.weight
|
159
|
+
end
|
160
|
+
|
161
|
+
m + connection_delta
|
162
|
+
end
|
151
163
|
end
|
152
|
-
|
164
|
+
|
165
|
+
step_one = sum || next
|
166
|
+
end
|
167
|
+
|
168
|
+
from_here = bptt_connecting_to neuron, network, timestep
|
169
|
+
neuron_stack |= from_here
|
153
170
|
|
154
171
|
node_delta =
|
155
172
|
ACTIVATION_DERIVATIVES[neuron.activation_function]
|
156
|
-
.call(states[timestep][:values][neuron.id])
|
157
|
-
|
173
|
+
.call(states[timestep][:values][neuron.id]) *
|
174
|
+
step_one
|
158
175
|
|
159
176
|
node_deltas[timestep][neuron.id] = node_delta
|
160
177
|
|
178
|
+
in_timestep = neuron.context? ? timestep - 1 : timestep
|
161
179
|
network.connections_to(neuron).each do |c|
|
162
|
-
in_timestep = neuron.context? ? timestep - 1 : timestep
|
163
|
-
|
164
180
|
# connection gradient is the output neuron delta multipled by the
|
165
181
|
# connection's input neuron value.
|
166
182
|
gradient =
|
167
183
|
if c.output_neuron.is_a? ProductNeuron
|
168
184
|
intermediate = states[timestep][:intermediates][c.output_neuron.id]
|
169
|
-
node_delta
|
185
|
+
node_delta * intermediate / c.weight
|
170
186
|
elsif c.input_neuron.context? && timestep == 0
|
171
187
|
0.to_d
|
172
188
|
else
|
173
|
-
node_delta
|
189
|
+
node_delta * states[in_timestep][:values][c.input_neuron.id]
|
174
190
|
end
|
175
191
|
|
176
192
|
gradients[c.id] += gradient
|
@@ -192,7 +208,7 @@ module RANN
|
|
192
208
|
end
|
193
209
|
end
|
194
210
|
|
195
|
-
def restore filepath
|
211
|
+
def restore filepath = nil
|
196
212
|
unless filepath
|
197
213
|
filepath = Dir['*'].select{ |f| f =~ /rann_savepoint_.*/ }.sort.last
|
198
214
|
|
@@ -217,7 +233,7 @@ module RANN
|
|
217
233
|
total_squared_error = 0.to_d
|
218
234
|
|
219
235
|
targets.size.times do |i|
|
220
|
-
total_squared_error += (targets[i] - outputs[i])
|
236
|
+
total_squared_error += (targets[i] - outputs[i]) ** 2 / 2
|
221
237
|
end
|
222
238
|
|
223
239
|
total_squared_error
|
@@ -231,11 +247,12 @@ module RANN
|
|
231
247
|
# halt traversal if we're at a context and we're at the base timestep
|
232
248
|
return [] if neuron.context? && timestep == 0
|
233
249
|
|
250
|
+
timestep -= 1 if neuron.context?
|
251
|
+
|
234
252
|
network.connections_to(neuron).each.with_object [] do |c, a|
|
235
253
|
# don't enqueue connections from inputs
|
236
254
|
next if c.input_neuron.input?
|
237
255
|
|
238
|
-
timestep -= timestep if neuron.context?
|
239
256
|
a << [c.input_neuron, timestep]
|
240
257
|
end
|
241
258
|
end
|
data/lib/rann/connection.rb
CHANGED
@@ -25,7 +25,7 @@ module RANN
|
|
25
25
|
|
26
26
|
def process
|
27
27
|
if processable? && !processed?
|
28
|
-
out_value = input_neuron.value
|
28
|
+
out_value = input_neuron.value * weight
|
29
29
|
output_neuron.push_value! out_value
|
30
30
|
@processed = true
|
31
31
|
end
|
@@ -60,8 +60,9 @@ module RANN
|
|
60
60
|
if output_neuron.context?
|
61
61
|
1.to_d
|
62
62
|
else
|
63
|
-
rand.to_d
|
63
|
+
rand.to_d BigDecimal.limit
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
68
|
+
|
@@ -1,15 +1,17 @@
|
|
1
1
|
module RANN
|
2
2
|
class GradientChecker
|
3
|
-
|
3
|
+
def self.epsilon
|
4
|
+
10.to_d ** -4
|
5
|
+
end
|
4
6
|
|
5
7
|
def self.check network, inputs, targets, dvec
|
6
8
|
gradapprox = []
|
7
9
|
|
8
10
|
network.params.size.times do |i|
|
9
11
|
thetaplus = network.params.dup
|
10
|
-
thetaplus[i] = thetaplus[i] +
|
12
|
+
thetaplus[i] = thetaplus[i] + epsilon
|
11
13
|
thetaminus = network.params.dup
|
12
|
-
thetaminus[i] = thetaminus[i] -
|
14
|
+
thetaminus[i] = thetaminus[i] - epsilon
|
13
15
|
|
14
16
|
network.impose thetaplus
|
15
17
|
outputs = network.evaluate inputs
|
@@ -21,7 +23,7 @@ module RANN
|
|
21
23
|
error_thetaminus = error outputs, targets
|
22
24
|
network.reset!
|
23
25
|
|
24
|
-
gradapprox[i] = (error_thetaplus - error_thetaminus)
|
26
|
+
gradapprox[i] = (error_thetaplus - error_thetaminus) / (epsilon * 2)
|
25
27
|
end
|
26
28
|
|
27
29
|
gradapprox.each.with_index.with_object [] do |(ga, i), res|
|
@@ -33,7 +35,7 @@ module RANN
|
|
33
35
|
total_squared_error = 0.to_d
|
34
36
|
|
35
37
|
targets.size.times do |i|
|
36
|
-
total_squared_error += (targets[i] - outputs[i])
|
38
|
+
total_squared_error += (targets[i] - outputs[i]) ** 2 / 2
|
37
39
|
end
|
38
40
|
|
39
41
|
total_squared_error
|
data/lib/rann/lstm.rb
CHANGED
@@ -19,52 +19,46 @@ module RANN
|
|
19
19
|
|
20
20
|
def init
|
21
21
|
@size.times do |j|
|
22
|
-
input = RANN::Neuron.new("LSTM #{name} Input #{j}", 0, :standard).tap{ |n| @network.add n }
|
22
|
+
input = RANN::Neuron.new("LSTM #{name} Input #{j}", 0, :standard, :linear).tap{ |n| @network.add n }
|
23
23
|
@inputs << input
|
24
24
|
|
25
25
|
f = RANN::Neuron.new("LSTM #{name} F #{j}", 3, :standard, :sig).tap{ |n| @network.add n }
|
26
26
|
i = RANN::Neuron.new("LSTM #{name} I #{j}", 4, :standard, :sig).tap{ |n| @network.add n }
|
27
27
|
g = RANN::Neuron.new("LSTM #{name} G #{j}", 3, :standard, :tanh).tap{ |n| @network.add n }
|
28
28
|
o = RANN::Neuron.new("LSTM #{name} O #{j}", 3, :standard, :sig).tap{ |n| @network.add n }
|
29
|
-
bias_f = RANN::Neuron.new("LSTM #{name} Bias F #{j}", 0, :bias).tap
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
@network.add n
|
34
|
-
end
|
35
|
-
bias_g = RANN::Neuron.new("LSTM #{name} Bias G #{j}", 0, :bias).tap do |n|
|
36
|
-
@network.add n
|
37
|
-
end
|
38
|
-
bias_o = RANN::Neuron.new("LSTM #{name} Bias O #{j}", 0, :bias).tap do |n|
|
39
|
-
@network.add n
|
40
|
-
end
|
29
|
+
bias_f = RANN::Neuron.new("LSTM #{name} Bias F #{j}", 0, :bias).tap{ |n| @network.add n }
|
30
|
+
bias_i = RANN::Neuron.new("LSTM #{name} Bias I #{j}", 0, :bias).tap{ |n| @network.add n }
|
31
|
+
bias_g = RANN::Neuron.new("LSTM #{name} Bias G #{j}", 0, :bias).tap{ |n| @network.add n }
|
32
|
+
bias_o = RANN::Neuron.new("LSTM #{name} Bias O #{j}", 0, :bias).tap{ |n| @network.add n }
|
41
33
|
memory_product = RANN::ProductNeuron.new("LSTM #{name} Mem Product #{j}", 2, :standard, :linear).tap{ |n| @network.add n }
|
42
34
|
i_g_product = RANN::ProductNeuron.new("LSTM #{name} Hidden 2/3 Product #{j}", 2, :standard, :linear).tap{ |n| @network.add n }
|
43
35
|
memory_standard = RANN::Neuron.new("LSTM #{name} Mem Standard #{j}", 2, :standard, :linear).tap{ |n| @network.add n }
|
44
36
|
memory_tanh = RANN::Neuron.new("LSTM #{name} Mem Tanh #{j}", 1, :standard, :tanh).tap{ |n| @network.add n }
|
45
37
|
memory_o_product = RANN::ProductNeuron.new("LSTM #{name} Mem/Hidden 4 Product #{j}", 2, :standard, :linear).tap{ |n| @network.add n }
|
46
|
-
|
47
|
-
|
48
|
-
|
38
|
+
@outputs << memory_o_product
|
39
|
+
memory_context =
|
40
|
+
RANN::Neuron.new("LSTM #{name} Mem Context #{j}", 1, :context).tap do |n|
|
41
|
+
@network.add n
|
42
|
+
n.value = 1.to_d # connecting to a product neuron
|
43
|
+
end
|
49
44
|
output_context = RANN::Neuron.new("LSTM #{name} Output Context #{j}", 1, :context).tap{ |n| @network.add n }
|
50
45
|
|
51
46
|
@network.add RANN::Connection.new input, f
|
52
47
|
@network.add RANN::Connection.new input, i
|
53
48
|
@network.add RANN::Connection.new input, g
|
54
49
|
@network.add RANN::Connection.new input, o
|
55
|
-
@network.add RANN::LockedConnection.new f, memory_product, 1
|
56
|
-
@network.add RANN::LockedConnection.new i, i_g_product, 1
|
57
|
-
@network.add RANN::LockedConnection.new g, i_g_product, 1
|
58
|
-
@network.add RANN::LockedConnection.new i_g_product, memory_standard, 1
|
59
|
-
@network.add RANN::LockedConnection.new memory_product, memory_standard, 1
|
60
|
-
@network.add RANN::LockedConnection.new memory_standard, memory_tanh, 1
|
61
|
-
@network.add RANN::LockedConnection.new o, memory_o_product, 1
|
62
|
-
@network.add RANN::LockedConnection.new memory_tanh, memory_o_product, 1
|
63
|
-
@network.add RANN::LockedConnection.new
|
64
|
-
@network.add RANN::LockedConnection.new
|
65
|
-
@network.add RANN::LockedConnection.new memory_context,
|
66
|
-
@network.add RANN::LockedConnection.new
|
67
|
-
@network.add RANN::LockedConnection.new memory_o_product, output_context, 1
|
50
|
+
@network.add RANN::LockedConnection.new f, memory_product, 1.to_d
|
51
|
+
@network.add RANN::LockedConnection.new i, i_g_product, 1.to_d
|
52
|
+
@network.add RANN::LockedConnection.new g, i_g_product, 1.to_d
|
53
|
+
@network.add RANN::LockedConnection.new i_g_product, memory_standard, 1.to_d
|
54
|
+
@network.add RANN::LockedConnection.new memory_product, memory_standard, 1.to_d
|
55
|
+
@network.add RANN::LockedConnection.new memory_standard, memory_tanh, 1.to_d
|
56
|
+
@network.add RANN::LockedConnection.new o, memory_o_product, 1.to_d
|
57
|
+
@network.add RANN::LockedConnection.new memory_tanh, memory_o_product, 1.to_d
|
58
|
+
@network.add RANN::LockedConnection.new memory_standard, memory_context, 1.to_d
|
59
|
+
@network.add RANN::LockedConnection.new memory_context, memory_product, 1.to_d
|
60
|
+
@network.add RANN::LockedConnection.new memory_context, i, 1.to_d
|
61
|
+
@network.add RANN::LockedConnection.new memory_o_product, output_context, 1.to_d
|
68
62
|
@network.add RANN::Connection.new output_context, f
|
69
63
|
@network.add RANN::Connection.new output_context, i
|
70
64
|
@network.add RANN::Connection.new output_context, g
|
@@ -78,7 +72,7 @@ module RANN
|
|
78
72
|
|
79
73
|
def add_input neuron
|
80
74
|
@inputs.each do |input|
|
81
|
-
@network.add RANN::
|
75
|
+
@network.add RANN::Connection.new neuron, input
|
82
76
|
end
|
83
77
|
end
|
84
78
|
end
|
data/lib/rann/network.rb
CHANGED
@@ -41,7 +41,7 @@ module RANN
|
|
41
41
|
# would probably be easier to detect circular dependency this way too?
|
42
42
|
begin
|
43
43
|
i = 0
|
44
|
-
until
|
44
|
+
until connections.select(&:enabled?).all? &:processed?
|
45
45
|
i += 1
|
46
46
|
connections.each do |connection|
|
47
47
|
next if !connection.enabled?
|
@@ -167,11 +167,15 @@ module RANN
|
|
167
167
|
|
168
168
|
def init_normalised!
|
169
169
|
connections.each do |c|
|
170
|
+
next if c.locked?
|
171
|
+
|
170
172
|
out_cons = c.output_neuron.connection_count.to_d
|
171
|
-
from = -1.to_d
|
172
|
-
to = 1.to_d
|
173
|
-
c.weight = (to - from)
|
173
|
+
from = -1.to_d / out_cons.sqrt(0)
|
174
|
+
to = 1.to_d / out_cons.sqrt(0)
|
175
|
+
c.weight = (to - from) * rand.to_d + from
|
174
176
|
end
|
177
|
+
|
178
|
+
true
|
175
179
|
end
|
176
180
|
alias init_normalized! init_normalised!
|
177
181
|
|
@@ -179,6 +183,16 @@ module RANN
|
|
179
183
|
neurons.each do |neuron|
|
180
184
|
neuron.connection_count = connections.count{ |c| c.output_neuron == neuron }
|
181
185
|
end
|
186
|
+
|
187
|
+
true
|
188
|
+
end
|
189
|
+
|
190
|
+
def neurons_with_no_outgoing_connections
|
191
|
+
return @no_outgoing if defined? @no_outgoing
|
192
|
+
|
193
|
+
neurons.select do |n|
|
194
|
+
connections_from(n).none?
|
195
|
+
end
|
182
196
|
end
|
183
197
|
end
|
184
198
|
end
|
data/lib/rann/neuron.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require "securerandom"
|
2
2
|
require "bigdecimal"
|
3
|
+
require "bigdecimal/math"
|
3
4
|
require "bigdecimal/util"
|
4
5
|
|
5
6
|
module RANN
|
6
7
|
class Neuron
|
7
8
|
ACTIVATION_FUNCTIONS = {
|
8
|
-
sig: ->(v){ 1.to_d
|
9
|
-
tanh: ->(v){ Math.tanh(v).to_d(
|
9
|
+
sig: ->(v){ 1.to_d / (1 + BigMath.E(BigDecimal.limit) ** -v) },
|
10
|
+
tanh: ->(v){ Math.tanh(v).to_d(BigDecimal.limit) },
|
10
11
|
relu: ->(v){ [0.to_d, v].max },
|
11
12
|
linear: ->(v){ v },
|
12
13
|
step: ->(v){ v > 0.5 ? 1.to_d : 0.to_d },
|
@@ -73,7 +74,7 @@ module RANN
|
|
73
74
|
end
|
74
75
|
|
75
76
|
def initial_activation_function
|
76
|
-
if standard?
|
77
|
+
if standard?
|
77
78
|
:relu
|
78
79
|
else
|
79
80
|
:linear
|
@@ -12,9 +12,9 @@ module RANN
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def update grad, cid
|
15
|
-
@historical_gradient[cid] = @historical_gradient[cid] + grad
|
15
|
+
@historical_gradient[cid] = @historical_gradient[cid] + grad ** 2
|
16
16
|
|
17
|
-
grad
|
17
|
+
grad * - @learning_rate / (@fudge_factor + @historical_gradient[cid].sqrt(0))
|
18
18
|
end
|
19
19
|
|
20
20
|
# anything that gets modified over the course of training
|
@@ -13,9 +13,9 @@ module RANN
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def update grad, cid
|
16
|
-
@historical_gradient[cid] = @decay
|
16
|
+
@historical_gradient[cid] = @decay * @historical_gradient[cid] + (1 - @decay) * grad ** 2
|
17
17
|
|
18
|
-
grad
|
18
|
+
grad * - @learning_rate / (@fudge_factor + @historical_gradient[cid].sqrt(0))
|
19
19
|
end
|
20
20
|
|
21
21
|
# anything that gets modified over the course of training
|
data/lib/rann/product_neuron.rb
CHANGED
data/lib/rann/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rann
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Campbell
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-12-
|
11
|
+
date: 2017-12-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parallel
|
@@ -110,6 +110,7 @@ files:
|
|
110
110
|
- bench/xor_benchmark.rb
|
111
111
|
- bin/console
|
112
112
|
- bin/setup
|
113
|
+
- examples/step_by_step.rb
|
113
114
|
- examples/xor.rb
|
114
115
|
- lib/rann.rb
|
115
116
|
- lib/rann/backprop.rb
|