CooCoo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/CooCoo.gemspec +47 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +88 -0
  6. data/README.md +123 -0
  7. data/Rakefile +81 -0
  8. data/bin/cuda-dev-info +25 -0
  9. data/bin/cuda-free +28 -0
  10. data/bin/cuda-free-trend +7 -0
  11. data/bin/ffi-gen +267 -0
  12. data/bin/spec_runner_html.sh +42 -0
  13. data/bin/trainer +198 -0
  14. data/bin/trend-cost +13 -0
  15. data/examples/char-rnn.rb +405 -0
  16. data/examples/cifar/cifar.rb +94 -0
  17. data/examples/img-similarity.rb +201 -0
  18. data/examples/math_ops.rb +57 -0
  19. data/examples/mnist.rb +365 -0
  20. data/examples/mnist_classifier.rb +293 -0
  21. data/examples/mnist_dream.rb +214 -0
  22. data/examples/seeds.rb +268 -0
  23. data/examples/seeds_dataset.txt +210 -0
  24. data/examples/t10k-images-idx3-ubyte +0 -0
  25. data/examples/t10k-labels-idx1-ubyte +0 -0
  26. data/examples/train-images-idx3-ubyte +0 -0
  27. data/examples/train-labels-idx1-ubyte +0 -0
  28. data/ext/buffer/Rakefile +50 -0
  29. data/ext/buffer/buffer.pre.cu +727 -0
  30. data/ext/buffer/matrix.pre.cu +49 -0
  31. data/lib/CooCoo.rb +1 -0
  32. data/lib/coo-coo.rb +18 -0
  33. data/lib/coo-coo/activation_functions.rb +344 -0
  34. data/lib/coo-coo/consts.rb +5 -0
  35. data/lib/coo-coo/convolution.rb +298 -0
  36. data/lib/coo-coo/core_ext.rb +75 -0
  37. data/lib/coo-coo/cost_functions.rb +91 -0
  38. data/lib/coo-coo/cuda.rb +116 -0
  39. data/lib/coo-coo/cuda/device_buffer.rb +240 -0
  40. data/lib/coo-coo/cuda/device_buffer/ffi.rb +109 -0
  41. data/lib/coo-coo/cuda/error.rb +51 -0
  42. data/lib/coo-coo/cuda/host_buffer.rb +117 -0
  43. data/lib/coo-coo/cuda/runtime.rb +157 -0
  44. data/lib/coo-coo/cuda/vector.rb +315 -0
  45. data/lib/coo-coo/data_sources.rb +2 -0
  46. data/lib/coo-coo/data_sources/xournal.rb +25 -0
  47. data/lib/coo-coo/data_sources/xournal/bitmap_stream.rb +197 -0
  48. data/lib/coo-coo/data_sources/xournal/document.rb +377 -0
  49. data/lib/coo-coo/data_sources/xournal/loader.rb +144 -0
  50. data/lib/coo-coo/data_sources/xournal/renderer.rb +101 -0
  51. data/lib/coo-coo/data_sources/xournal/saver.rb +99 -0
  52. data/lib/coo-coo/data_sources/xournal/training_document.rb +78 -0
  53. data/lib/coo-coo/data_sources/xournal/training_document/constants.rb +15 -0
  54. data/lib/coo-coo/data_sources/xournal/training_document/document_maker.rb +89 -0
  55. data/lib/coo-coo/data_sources/xournal/training_document/document_reader.rb +105 -0
  56. data/lib/coo-coo/data_sources/xournal/training_document/example.rb +37 -0
  57. data/lib/coo-coo/data_sources/xournal/training_document/sets.rb +76 -0
  58. data/lib/coo-coo/debug.rb +8 -0
  59. data/lib/coo-coo/dot.rb +129 -0
  60. data/lib/coo-coo/drawing.rb +4 -0
  61. data/lib/coo-coo/drawing/cairo_canvas.rb +100 -0
  62. data/lib/coo-coo/drawing/canvas.rb +68 -0
  63. data/lib/coo-coo/drawing/chunky_canvas.rb +101 -0
  64. data/lib/coo-coo/drawing/sixel.rb +214 -0
  65. data/lib/coo-coo/enum.rb +17 -0
  66. data/lib/coo-coo/from_name.rb +58 -0
  67. data/lib/coo-coo/fully_connected_layer.rb +205 -0
  68. data/lib/coo-coo/generation_script.rb +38 -0
  69. data/lib/coo-coo/grapher.rb +140 -0
  70. data/lib/coo-coo/image.rb +286 -0
  71. data/lib/coo-coo/layer.rb +67 -0
  72. data/lib/coo-coo/layer_factory.rb +26 -0
  73. data/lib/coo-coo/linear_layer.rb +59 -0
  74. data/lib/coo-coo/math.rb +607 -0
  75. data/lib/coo-coo/math/abstract_vector.rb +121 -0
  76. data/lib/coo-coo/math/functions.rb +39 -0
  77. data/lib/coo-coo/math/interpolation.rb +7 -0
  78. data/lib/coo-coo/network.rb +264 -0
  79. data/lib/coo-coo/neuron.rb +112 -0
  80. data/lib/coo-coo/neuron_layer.rb +168 -0
  81. data/lib/coo-coo/option_parser.rb +18 -0
  82. data/lib/coo-coo/platform.rb +17 -0
  83. data/lib/coo-coo/progress_bar.rb +11 -0
  84. data/lib/coo-coo/recurrence/backend.rb +99 -0
  85. data/lib/coo-coo/recurrence/frontend.rb +101 -0
  86. data/lib/coo-coo/sequence.rb +187 -0
  87. data/lib/coo-coo/shell.rb +2 -0
  88. data/lib/coo-coo/temporal_network.rb +291 -0
  89. data/lib/coo-coo/trainer.rb +21 -0
  90. data/lib/coo-coo/trainer/base.rb +67 -0
  91. data/lib/coo-coo/trainer/batch.rb +82 -0
  92. data/lib/coo-coo/trainer/batch_stats.rb +27 -0
  93. data/lib/coo-coo/trainer/momentum_stochastic.rb +59 -0
  94. data/lib/coo-coo/trainer/stochastic.rb +47 -0
  95. data/lib/coo-coo/transformer.rb +272 -0
  96. data/lib/coo-coo/vector_layer.rb +194 -0
  97. data/lib/coo-coo/version.rb +3 -0
  98. data/lib/coo-coo/weight_deltas.rb +23 -0
  99. data/prototypes/convolution.rb +116 -0
  100. data/prototypes/linear_drop.rb +51 -0
  101. data/prototypes/recurrent_layers.rb +79 -0
  102. data/www/images/screamer.png +0 -0
  103. data/www/images/screamer.xcf +0 -0
  104. data/www/index.html +82 -0
  105. metadata +373 -0
@@ -0,0 +1,49 @@
1
+ #include <stdio.h>
2
+ #include <math.h>
3
+
4
+ #include "public.h"
5
+ #include "buffer.h"
6
+ #include "matrix.h"
7
+
8
+ #ifdef IN_PUBLIC
9
+ typedef struct Matrix2D_s
10
+ {
11
+ size_t width, height;
12
+ Buffer buffer;
13
+ double **rows;
14
+ } *Matrix2D;
15
+ #endif
16
+
17
+ PUBLIC Matrix2D matrix_new(Buffer b, size_t w, size_t h)
18
+ {
19
+ if(w * h >= b->length) {
20
+ return NULL;
21
+ }
22
+
23
+ Matrix2D m = (Matrix2D)malloc(sizeof(Matrix2D_s));
24
+ m->width = w;
25
+ m->height = h;
26
+ m->buffer = b;
27
+ m->rows = (double **)malloc(sizeof(double) * h);
28
+
29
+ for(size_t i = 0; i < h; i++) {
30
+ m->rows[i] = b->data + (i * w);
31
+ }
32
+
33
+ return m;
34
+ }
35
+
36
+ PUBLIC void matrix_free(Matrix2D m)
37
+ {
38
+ for(size_t i = 0; i < m->height; i++) {
39
+ free(m->rows[i]);
40
+ m->rows[i] = NULL;
41
+ }
42
+
43
+ free(m);
44
+ }
45
+
46
+ PUBLIC Buffer matrix_buffer(Matrix2D m)
47
+ {
48
+ return m->buffer;
49
+ }
@@ -0,0 +1 @@
1
+ require 'coo-coo'
@@ -0,0 +1,18 @@
1
+ require 'coo-coo/platform'
2
+ require 'coo-coo/consts'
3
+ require 'coo-coo/math'
4
+ require 'coo-coo/neuron'
5
+ require 'coo-coo/layer'
6
+ require 'coo-coo/network'
7
+ require 'coo-coo/sequence'
8
+ require 'coo-coo/temporal_network'
9
+ require 'coo-coo/convolution'
10
+ require 'coo-coo/recurrence/frontend'
11
+ require 'coo-coo/trainer'
12
+ require 'coo-coo/progress_bar'
13
+ require 'coo-coo/activation_functions'
14
+ require 'coo-coo/cost_functions'
15
+ require 'coo-coo/option_parser'
16
+ require 'coo-coo/generation_script'
17
+ require 'coo-coo/drawing'
18
+ require 'coo-coo/data_sources'
@@ -0,0 +1,344 @@
1
+ require 'singleton'
2
+ require 'coo-coo/from_name'
3
+
4
+ module CooCoo
5
+ # Activation functions are functions of a single variable used by some
6
+ # {Layer}s to introduce non-linearities into or to alter data from a
7
+ # previous layer.
8
+ #
9
+ # To get an activation function instance use the included {#from_name}.
10
+ # From there you can call the methods found on the {Identity} activation
11
+ # function on any activation function.
12
+ #
13
+ # To create a new activation function that can be used in stored networks,
14
+ # you must subclass {Identity} and call {ActivationFunctions.register}.
15
+ module ActivationFunctions
16
+ class << self
17
+ include FromName
18
+ end
19
+
20
+ # The base for all the ActivationFunctions. Implements a do nothing
21
+ # activation function for a {Layer}.
22
+ class Identity
23
+ include Singleton
24
+ ActivationFunctions.register(self)
25
+
26
+ # Forwards missing class methods to the #instance.
27
+ def self.method_missing(mid, *args, &block)
28
+ instance.send(mid, *args, &block)
29
+ end
30
+
31
+ # A file friendly name for the activation function.
32
+ def name
33
+ self.class.name.split("::").last
34
+ end
35
+
36
+ def to_s
37
+ name
38
+ end
39
+
40
+ # Perform the activation.
41
+ # @param x [Numeric, Vector]
42
+ # @return [Numeric, Vector]
43
+ def call(x)
44
+ x
45
+ end
46
+
47
+ # Calculate the derivative at +x+.
48
+ # @param x [Numeric, Vector]
49
+ # @param y [Numeric, Vector, nil] Optional precomputed return value from #call.
50
+ def derivative(x, y = nil)
51
+ if (y || x).kind_of?(Numeric)
52
+ 1.0
53
+ else
54
+ (y || x).class.ones((y || x).size)
55
+ end
56
+ end
57
+
58
+ # Initial weights a {Layer} should use when using this function.
59
+ # @param num_inputs [Integer] Number of inputs into the {Layer}
60
+ # @param size [Integer] The size or number of outputs of the {Layer}.
61
+ # @return [Vector] of weights that are randomly distributed
62
+ # between -1.0 and 1.0.
63
+ def initial_weights(num_inputs, size)
64
+ (CooCoo::Vector.rand(num_inputs * size) * 2.0 - 1.0) / num_inputs.to_f.sqrt
65
+ end
66
+
67
+ # Initial bias for a {Layer}.
68
+ # @param size [Integer] Number of bias elements to return.
69
+ # @return [Vector]
70
+ def initial_bias(size)
71
+ CooCoo::Vector.ones(size)
72
+ end
73
+
74
+ # Adjusts a {Network}'s inputs to the domain of the function.
75
+ # @param x [Vector]
76
+ # @return [Vector]
77
+ def prep_input(x)
78
+ x
79
+ end
80
+
81
+ # Adjusts a training set's target domain from +0..1+ to domain of the
82
+ # function's output.
83
+ # @param x [Vector]
84
+ # @return [Vector]
85
+ def prep_output_target(x)
86
+ x
87
+ end
88
+ end
89
+
90
+ class Logistic < Identity
91
+ ActivationFunctions.register(self)
92
+
93
+ def call(x)
94
+ 1.0 / ( 1.0 + (-x).exp)
95
+ end
96
+
97
+ def derivative(x, y = nil)
98
+ y ||= call(x)
99
+ y * (1.0 - y)
100
+ end
101
+ end
102
+
103
+ class TanH < Identity
104
+ ActivationFunctions.register(self)
105
+
106
+ def call(x)
107
+ 2.0 / (1.0 + (x * -2.0).exp) - 1.0
108
+ end
109
+
110
+ def derivative(x, y = nil)
111
+ y ||= call(x)
112
+ 1.0 - y * y
113
+ end
114
+
115
+ def initial_bias(size)
116
+ CooCoo::Vector.zeros(size)
117
+ end
118
+
119
+ def prep_input(arr)
120
+ (arr.minmax_normalize(true) * 2.0) - 1.0
121
+ end
122
+
123
+ def prep_output_target(arr)
124
+ prep_input(arr)
125
+ end
126
+ end
127
+
128
+ class ReLU < Identity
129
+ ActivationFunctions.register(self)
130
+
131
+ def call(x)
132
+ t = x > 0
133
+ if t.kind_of?(FalseClass)
134
+ 0.0
135
+ elsif t.kind_of?(TrueClass)
136
+ x
137
+ else
138
+ x * t
139
+ end
140
+ end
141
+
142
+ def derivative(x, y = nil)
143
+ y ||= call(x)
144
+ t = y > 0
145
+ if t.kind_of?(FalseClass)
146
+ 0.0
147
+ elsif t.kind_of?(TrueClass)
148
+ 1.0
149
+ else
150
+ t
151
+ end
152
+ end
153
+
154
+ def initial_weights(num_inputs, size)
155
+ CooCoo::Vector.rand(num_inputs * size) * (2.0 / (num_inputs * size).to_f).sqrt
156
+ end
157
+ end
158
+
159
+ class LeakyReLU < Identity
160
+ ActivationFunctions.register(self)
161
+ public_class_method :new
162
+
163
+ def initialize(pos = 1.0, neg = 0.0001)
164
+ @positive_coeff = pos.to_f
165
+ @negative_coeff = neg.to_f
166
+ end
167
+
168
+ attr_accessor :positive_coeff
169
+ attr_accessor :negative_coeff
170
+
171
+ def call(x)
172
+ pos = x > 0
173
+
174
+ if pos.kind_of?(FalseClass)
175
+ x * @negative_coeff
176
+ elsif pos.kind_of?(TrueClass)
177
+ x * @positive_coeff
178
+ else
179
+ neg = x <= 0
180
+ (x * pos * @positive_coeff) + (x * neg * @negative_coeff)
181
+ end
182
+ end
183
+
184
+ def derivative(x, y = nil)
185
+ y ||= call(x)
186
+ pos = y > 0
187
+ if pos.kind_of?(FalseClass)
188
+ @negative_coeff
189
+ elsif pos.kind_of?(TrueClass)
190
+ @positive_coeff
191
+ else
192
+ neg = y <= 0
193
+ (pos * @positive_coeff) + (neg * @negative_coeff)
194
+ end
195
+ end
196
+
197
+ def initial_weights(num_inputs, size)
198
+ CooCoo::Vector.rand(num_inputs * size) * (2.0 / (num_inputs * size).to_f).sqrt
199
+ end
200
+
201
+ def ==(other)
202
+ other.kind_of?(self.class) &&
203
+ positive_coeff == other.positive_coeff &&
204
+ negative_coeff == other.negative_coeff
205
+ end
206
+ end
207
+
208
+ # Computes the Softmax function given a {Vector}:
209
+ # y_i = e ** x_i / sum(e ** x)
210
+ # @see https://deepnotes.io/softmax-crossentropy
211
+ # @see https://becominghuman.ai/back-propagation-is-very-simple-who-made-it-complicated-97b794c97e5c
212
+ class SoftMax < Identity
213
+ ActivationFunctions.register(self)
214
+
215
+ def call(x)
216
+ e = x.exp
217
+ e / e.sum
218
+ end
219
+
220
+ def derivative(x, y = nil)
221
+ y ||= call(x)
222
+ s = x.exp.sum
223
+ y * (s - x) / s
224
+ end
225
+ end
226
+
227
+ # Computes the Softmax function given a {Vector} but subtracts the
228
+ # maximum value from every element prior to Softmax to prevent overflows:
229
+ # y_i = e ** (x_i - max(x)) / sum(e ** (x - max(x)))
230
+ class ShiftedSoftMax < SoftMax
231
+ ActivationFunctions.register(self)
232
+
233
+ def call(x)
234
+ super(x - x.max)
235
+ end
236
+
237
+ def derivative(x, y = nil)
238
+ super(x - x.max, y)
239
+ end
240
+ end
241
+
242
+ class MinMax < Identity
243
+ ActivationFunctions.register(self)
244
+
245
+ def call(x)
246
+ if x.respond_to?(:minmax_normalize)
247
+ x.minmax_normalize
248
+ else
249
+ x
250
+ end
251
+ end
252
+
253
+ def derivative(x, y = nil)
254
+ min, max = x.minmax
255
+ (y || x).class.new((y || x).size, 1.0 / (max - min))
256
+ end
257
+
258
+ def prep_output_target(x)
259
+ x.minmax_normalize(true)
260
+ end
261
+ end
262
+
263
+ # Like the {MinMax} but safe when the input is all the same value.
264
+ class ZeroSafeMinMax < Identity
265
+ ActivationFunctions.register(self)
266
+
267
+ def call(x)
268
+ if x.respond_to?(:minmax_normalize)
269
+ x.minmax_normalize(true)
270
+ else
271
+ x
272
+ end
273
+ end
274
+
275
+ def derivative(x, y = nil)
276
+ min, max = x.minmax
277
+ delta = max - min
278
+ if delta == 0.0
279
+ x.zero
280
+ else
281
+ (y || x).class.new((y || x).size, 1.0 / (max - min))
282
+ end
283
+ end
284
+
285
+ def prep_output_target(x)
286
+ call(x)
287
+ end
288
+ end
289
+
290
+ class Normalize < Identity
291
+ ActivationFunctions.register(self)
292
+
293
+ def call(x)
294
+ if x.respond_to?(:normalize)
295
+ x.normalize
296
+ else
297
+ x.coerce(0)
298
+ end
299
+ end
300
+
301
+ def derivative(x, y = nil)
302
+ mag = x.magnitude()
303
+ y ||= call(x)
304
+ 1.0 / mag - y * y / mag
305
+ end
306
+
307
+ def prep_output_target(x)
308
+ x.normalize
309
+ end
310
+ end
311
+
312
+ # Like the {Normalize} but safe when the input is all the same value.
313
+ class ZeroSafeNormalize < Identity
314
+ ActivationFunctions.register(self)
315
+
316
+ def call(x)
317
+ if x.respond_to?(:normalize)
318
+ m = x.magnitude
319
+ if m == 0.0
320
+ 0.0
321
+ else
322
+ x / magnitude
323
+ end
324
+ else
325
+ x.coerce(0)
326
+ end
327
+ end
328
+
329
+ def derivative(x, y = nil)
330
+ mag = x.magnitude()
331
+ if mag == 0.0
332
+ 0.0
333
+ else
334
+ y ||= call(x)
335
+ 1.0 / mag - y * y / mag
336
+ end
337
+ end
338
+
339
+ def prep_output_target(x)
340
+ x.normalize
341
+ end
342
+ end
343
+ end
344
+ end
@@ -0,0 +1,5 @@
1
+ module CooCoo
2
+ def self.default_activation
3
+ CooCoo::ActivationFunctions::Logistic.instance
4
+ end
5
+ end
@@ -0,0 +1,298 @@
1
+ require 'coo-coo/layer_factory'
2
+
3
+ module CooCoo
4
+ module Convolution
5
+ class BoxLayer
6
+ LayerFactory.register_type(self)
7
+
8
+ attr_reader :width
9
+ attr_reader :height
10
+ attr_reader :horizontal_step
11
+ attr_reader :vertical_step
12
+ attr_reader :input_width
13
+ attr_reader :input_height
14
+ attr_reader :int_output_width
15
+ attr_reader :int_output_height
16
+ attr_reader :internal_layer
17
+ attr_reader :delta_accumulator
18
+
19
+ def initialize(width, height, horizontal_step, vertical_step, internal_layer, input_width, input_height, int_output_width, int_output_height, update_weights_with = :average)
20
+ @internal_layer = internal_layer
21
+ @width = width
22
+ @height = height
23
+ @horizontal_step = horizontal_step
24
+ @vertical_step = vertical_step
25
+ @input_width = input_width
26
+ @input_height = input_height
27
+ raise ArgumentError.new("Input size mismatch: #{input_width * input_height} is not #{internal_layer.num_inputs}") if internal_layer.num_inputs != (input_width * input_height)
28
+ @int_output_width = int_output_width
29
+ @int_output_height = int_output_height
30
+ raise ArgumentError.new("Input size mismatch: #{int_output_width * int_output_height} is not #{internal_layer.size}") if internal_layer.size != (int_output_width * int_output_height)
31
+ @delta_accumulator = delta_accumulator || :average
32
+ raise ArgumentError.new("Weights delta accumulator can only be averaged or summed") unless [ :average, :sum ].include?(@delta_accumulator)
33
+ end
34
+
35
+ def activation_function
36
+ internal_layer.activation_function
37
+ end
38
+
39
+ def horizontal_span
40
+ @horizontal_span ||= (@width / @horizontal_step.to_f).ceil
41
+ end
42
+
43
+ def vertical_span
44
+ @vertical_span ||= (@height / @vertical_step.to_f).ceil
45
+ end
46
+
47
+ def num_inputs
48
+ @width * @height
49
+ end
50
+
51
+ def output_width
52
+ (horizontal_span * int_output_width).to_i
53
+ end
54
+
55
+ def output_height
56
+ (vertical_span * int_output_height).to_i
57
+ end
58
+
59
+ def size
60
+ output_height * output_width
61
+ end
62
+
63
+ def neurons
64
+ internal_layer.neurons
65
+ end
66
+
67
+ def flatten_areas(outputs, w, h, inner_width)
68
+ out = CooCoo::Vector.new(w * h)
69
+
70
+ each_area do |grid_x, grid_y|
71
+ area_output = outputs[grid_y][grid_x]
72
+ gx = grid_x * w / horizontal_span.to_f
73
+ gy = grid_y * h / vertical_span.to_f
74
+ out.set2d!(w, area_output, inner_width, gx, gy)
75
+ end
76
+
77
+ out
78
+ end
79
+
80
+ def forward(input, hidden_state)
81
+ hs = hidden_state[self] || Array.new
82
+ outputs = each_area do |grid_x, grid_y|
83
+ hs_index = (grid_y * horizontal_span + grid_x).to_i
84
+ output, layer_hs = @internal_layer.forward(slice_input(input, grid_x, grid_y), hs[hs_index])
85
+ hs[hs_index] = layer_hs
86
+ output
87
+ end
88
+ hidden_state[self] = hs
89
+ [ flatten_areas(outputs, horizontal_span * int_output_width, vertical_span * int_output_height, int_output_width), hidden_state ]
90
+ end
91
+
92
+ def backprop(input, output, errors, hidden_state)
93
+ hs = hidden_state[self] || Array.new
94
+ deltas = each_area do |grid_x, grid_y|
95
+ hs_index = grid_y * horizontal_span + grid_x
96
+ d, layer_hs = @internal_layer.backprop(slice_input(input, grid_x, grid_y), slice_output(output, grid_x, grid_y), slice_output(errors, grid_x, grid_y), hs[hs_index])
97
+ hs[hs_index] = layer_hs
98
+ d
99
+ end
100
+ hidden_state[self] = hs
101
+ [ Sequence[deltas.collect { |d| Sequence[d] }], hidden_state ]
102
+ end
103
+
104
+ def transfer_error(deltas)
105
+ flatten_areas(each_area do |grid_x, grid_y|
106
+ @internal_layer.transfer_error(deltas[grid_y][grid_x]).to_a
107
+ end, width, height, input_width)
108
+ end
109
+
110
+ def update_weights!(inputs, deltas)
111
+ adjust_weights!(*weight_deltas(inputs, deltas))
112
+ end
113
+
114
+ def adjust_weights!(deltas)
115
+ @internal_layer.adjust_weights!(deltas)
116
+ self
117
+ end
118
+
119
+ def weight_deltas(inputs, deltas)
120
+ #rate = rate / (@horizontal_span * @vertical_span).to_f
121
+ change = []
122
+ wd = []
123
+
124
+ d = []
125
+ each_area do |grid_x, grid_y|
126
+ hs_index = grid_y * horizontal_span + grid_x
127
+ delta, hs = @internal_layer.
128
+ weight_deltas(slice_input(inputs, grid_x, grid_y),
129
+ deltas[grid_y][grid_x])
130
+ d << delta
131
+ end
132
+
133
+ Sequence[d].send(@delta_accumulator)
134
+ end
135
+
136
+ def ==(other)
137
+ other.kind_of?(self.class) &&
138
+ width == other.width &&
139
+ height == other.height &&
140
+ horizontal_step == other.horizontal_step &&
141
+ vertical_step == other.vertical_step &&
142
+ input_width == other.input_width &&
143
+ input_height == other.input_height &&
144
+ int_output_width == other.int_output_width &&
145
+ int_output_height == other.int_output_height &&
146
+ internal_layer == other.internal_layer &&
147
+ delta_accumulator == other.delta_accumulator
148
+ end
149
+
150
+ def to_hash(network = nil)
151
+ { type: self.class.to_s,
152
+ width: @width,
153
+ height: @height,
154
+ horizontal_step: @horizontal_step,
155
+ vertical_step: @vertical_step,
156
+ input_width: @input_width,
157
+ input_height: @input_height,
158
+ int_output_width: @int_output_width,
159
+ int_output_height: @int_output_height,
160
+ delta_accumulator: @delta_accumulator,
161
+ internal_layer: @internal_layer.to_hash(network)
162
+ }
163
+ end
164
+
165
+ def self.from_hash(h, network = nil)
166
+ self.new(h.fetch(:width), h.fetch(:height),
167
+ h.fetch(:horizontal_step), h.fetch(:vertical_step),
168
+ LayerFactory.from_hash(h.fetch(:internal_layer)),
169
+ h.fetch(:input_width), h.fetch(:input_height),
170
+ h.fetch(:int_output_width), h.fetch(:int_output_height),
171
+ h.fetch(:delta_accumulator, :average))
172
+ end
173
+
174
+ #private
175
+
176
+ def each_area
177
+ return to_enum(:each_area) unless block_given?
178
+
179
+ vertical_span.to_i.times.collect do |grid_y|
180
+ horizontal_span.to_i.times.collect do |grid_x|
181
+ yield(grid_x, grid_y)
182
+ end
183
+ end
184
+ end
185
+
186
+ def slice_input(input, grid_x, grid_y)
187
+ origin_x = grid_x * @horizontal_step
188
+ origin_y = grid_y * @vertical_step
189
+ input.slice_2d(@width,
190
+ @height,
191
+ origin_x, origin_y,
192
+ @input_width, @input_height,
193
+ 0.0)
194
+ end
195
+
196
+ def slice_output(output, grid_x, grid_y)
197
+ origin_x = grid_x * @int_output_width
198
+ origin_y = grid_y * @int_output_height
199
+ output.slice_2d((horizontal_span * @int_output_width).to_i,
200
+ (vertical_span * @int_output_height).to_i,
201
+ origin_x, origin_y,
202
+ @int_output_width, @int_output_height,
203
+ 0.0)
204
+ end
205
+ end
206
+ end
207
+ end
208
+
209
+ if __FILE__ == $0
210
+ require 'coo-coo/layer'
211
+ require 'coo-coo/cost_functions'
212
+
213
+ WIDTH = 16
214
+ HEIGHT = 16
215
+ X_STEP = 4
216
+ Y_STEP = 4
217
+ CONV_WIDTH = 4
218
+ CONV_HEIGHT = 4
219
+ CONV_OUT_WIDTH = 1
220
+ CONV_OUT_HEIGHT = 1
221
+ activation = CooCoo::ActivationFunctions.from_name(ENV.fetch('ACTIVATION', 'Logistic'))
222
+ cost_function = CooCoo::CostFunctions.from_name(ENV.fetch('COST', 'MeanSquare'))
223
+
224
+ inner_layer = CooCoo::Layer.new(CONV_WIDTH * CONV_HEIGHT, CONV_OUT_WIDTH * CONV_OUT_HEIGHT, activation)
225
+ layer = CooCoo::Convolution::BoxLayer.new(WIDTH, HEIGHT, X_STEP, Y_STEP, inner_layer, CONV_WIDTH, CONV_HEIGHT, CONV_OUT_WIDTH, CONV_OUT_HEIGHT)
226
+
227
+ INPUT_SIZE = layer.num_inputs
228
+ OUT_WIDTH = layer.output_width
229
+ OUT_HEIGHT = layer.output_height
230
+ OUTPUT_SIZE = layer.size
231
+ learning_rate = ENV.fetch('RATE', 0.3).to_f
232
+
233
+ input = [ 1.0 ] + (INPUT_SIZE - 2).times.collect { 0.0 } + [ 1.0 ]
234
+ input = CooCoo::Vector[input, INPUT_SIZE]
235
+ target = CooCoo::Vector.zeros(OUTPUT_SIZE)
236
+ target[0] = 1.0
237
+ target[-1] = 1.0
238
+
239
+ input = activation.prep_input(input)
240
+ target = activation.prep_input(target)
241
+
242
+ #input = (input - 0.5) * 2.0
243
+ #target = (target - 0.5) * 2.0
244
+
245
+ def matrix_image(m, width)
246
+ puts("matrix image #{width}")
247
+ s = m.to_a.each_slice(width).collect do |line|
248
+ line.collect do |c|
249
+ if c > 0.75
250
+ '#'
251
+ elsif c > 0.5
252
+ 'X'
253
+ elsif c > 0.25
254
+ 'x'
255
+ elsif c >= 0.0
256
+ '.'
257
+ elsif c >= -0.5
258
+ '-'
259
+ else
260
+ '~'
261
+ end
262
+ end.join
263
+ end.join("\n")
264
+ end
265
+
266
+ require 'benchmark'
267
+
268
+ Benchmark.bm(3) do |bm|
269
+ bm.report("loops") do
270
+ ENV.fetch("LOOPS", 100).to_i.times do |i|
271
+ puts("#{i}\n========\n")
272
+ #puts("Inputs =\n#{matrix_image(input, WIDTH)}")
273
+ output, hs = layer.forward(input, {})
274
+ #puts("Output = #{output}\n#{matrix_image(output, OUT_WIDTH)}")
275
+ err = cost_function.derivative(target, output)
276
+ #puts("Target = #{target}\n#{matrix_image(target, OUT_WIDTH)}")
277
+ #puts("Err = #{err}\n#{matrix_image(err * 10.0, OUT_WIDTH)}")
278
+ puts("|Err| = #{err.magnitude} #{(err * err).magnitude}")
279
+ deltas, hs = layer.backprop(input, output, err, hs)
280
+ #puts("Deltas = #{deltas}\n#{matrix_image(deltas, OUT_WIDTH)}")
281
+ xfer = layer.transfer_error(deltas)
282
+ #puts("Xfer error = #{xfer}\n#{matrix_image(xfer, OUT_WIDTH)}")
283
+ layer.update_weights!(input, deltas * learning_rate)
284
+ #puts("Weights updated")
285
+ output, hs = layer.forward(input, {})
286
+ puts("New output = #{output}\n#{matrix_image(output, OUT_WIDTH)}")
287
+ end
288
+ end
289
+ end
290
+
291
+ # layer.each_area do |x, y|
292
+ # puts("#{x}, #{y}\t#{x * CONV_WIDTH}, #{y * CONV_HEIGHT}")
293
+ # puts(matrix_image(layer.slice_input(input, x, y), CONV_WIDTH))
294
+ # puts
295
+ # puts(matrix_image(layer.slice_output(target, x, y), CONV_OUT_WIDTH))
296
+ # puts
297
+ # end
298
+ end