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,121 @@
1
+ module CooCoo
2
+ module Math
3
+ class AbstractVector
4
+ def self.rand(length, range = nil)
5
+ new(length) do |i|
6
+ args = [ range ] if range
7
+ Random.rand(*args)
8
+ end
9
+ end
10
+
11
+ def self.zeros(length)
12
+ new(length, 0.0)
13
+ end
14
+
15
+ def self.ones(length)
16
+ new(length, 1.0)
17
+ end
18
+
19
+ def zero
20
+ self.class.zeros(size)
21
+ end
22
+
23
+ def max
24
+ minmax[1]
25
+ end
26
+
27
+ def min
28
+ minmax[0]
29
+ end
30
+
31
+ def minmax
32
+ each.minmax
33
+ end
34
+
35
+ def minmax_normalize(use_zeros = false)
36
+ min, max = minmax
37
+ delta = (max - min)
38
+ if use_zeros && delta == 0.0
39
+ zero
40
+ else
41
+ (self - min) / delta
42
+ end
43
+ end
44
+
45
+ [ :log, :log2, :log10, :sqrt ].each do |op|
46
+ define_method(op) do
47
+ self.class[each.collect(&op)]
48
+ end
49
+ end
50
+
51
+ def slice_2d(src_width, src_height, origin_x, origin_y, width, height, initial = 0.0)
52
+ samples = height.times.collect do |y|
53
+ py = origin_y + y
54
+
55
+ width.times.collect do |x|
56
+ px = origin_x + x
57
+ if px >= 0 && px < src_width
58
+ i = py * src_width + px
59
+ if i >= 0 && i < size
60
+ self[i]
61
+ else
62
+ initial
63
+ end
64
+ else
65
+ initial
66
+ end
67
+ end
68
+ end.flatten
69
+
70
+ self.class[samples]
71
+ end
72
+
73
+ def set2d!(width, src, src_width, x, y)
74
+ raise ArgumentError.new("src's size needs to be a multiple of the width") if src.kind_of?(self.class) && src.size % src_width > 0
75
+
76
+ src.each_slice(src_width).with_index do |row, i|
77
+ index = (y+i) * width + x
78
+ next if index >= size
79
+ row.each_with_index do |p, px|
80
+ break if (x + px) >= width
81
+ self[index.to_i + px] = p
82
+ end
83
+ end
84
+
85
+ self
86
+ end
87
+
88
+ def collect_equal?(n)
89
+ if n.respond_to?(:each)
90
+ self.class[each.zip(n).collect { |a, b| a == b ? 1.0 : 0.0 }]
91
+ else
92
+ self.class[each.collect { |e| e == n ? 1.0 : 0.0 }]
93
+ end
94
+ end
95
+
96
+ def collect_not_equal?(n)
97
+ if n.respond_to?(:each)
98
+ self.class[each.zip(n).collect { |a, b| a != b ? 1.0 : 0.0 }]
99
+ else
100
+ self.class[each.collect { |e| e != n ? 1.0 : 0.0 }]
101
+ end
102
+ end
103
+
104
+ def collect_nan?
105
+ self.class[each.collect { |e| e.nan? ? 1.0 : 0.0 }]
106
+ end
107
+
108
+ def nan?
109
+ each.any?(&:nan?)
110
+ end
111
+
112
+ def collect_infinite?
113
+ self.class[each.collect { |e| e.infinite? ? 1.0 : 0.0 }]
114
+ end
115
+
116
+ def infinite?
117
+ each.any?(&:infinite?)
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,39 @@
1
+ module CooCoo
2
+ module Math
3
+ class << self
4
+ def max(a, b)
5
+ if a
6
+ if b
7
+ (a >= b) ? a : b
8
+ else
9
+ a
10
+ end
11
+ else
12
+ b
13
+ end
14
+ end
15
+
16
+ def min(a, b)
17
+ if a
18
+ if b
19
+ (a <= b) ? a : b
20
+ else
21
+ a
22
+ end
23
+ else
24
+ b
25
+ end
26
+ end
27
+
28
+ def clamp(n, min, max)
29
+ if n < min
30
+ min
31
+ elsif n > max
32
+ max
33
+ else
34
+ n
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,7 @@
1
+ module CooCoo
2
+ module Math
3
+ def self.lerp(a, b, t)
4
+ a * (1.0 - t) + b * t
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,264 @@
1
+ require 'yaml'
2
+ require 'coo-coo/consts'
3
+ require 'coo-coo/debug'
4
+ require 'coo-coo/core_ext'
5
+ require 'coo-coo/math'
6
+ require 'coo-coo/layer'
7
+ require 'coo-coo/enum'
8
+ require 'coo-coo/cost_functions'
9
+
10
+ module CooCoo
11
+ class Network
12
+ attr_reader :age, :activation_function
13
+ attr_accessor :command, :comments
14
+
15
+ def initialize
16
+ @layers = Array.new
17
+ @age = 0
18
+ @command = [ $0 ] + ARGV
19
+ yield(self) if block_given?
20
+ end
21
+
22
+ def num_inputs
23
+ @layers.first.num_inputs
24
+ end
25
+
26
+ def num_outputs
27
+ @layers.last.size
28
+ end
29
+
30
+ def num_layers
31
+ @layers.size
32
+ end
33
+
34
+ def layers
35
+ @layers
36
+ end
37
+
38
+ def layer_index(layer)
39
+ @layers.find_index { |l| l.eql?(layer) }
40
+ end
41
+
42
+ def layer(new_layer)
43
+ @layers << new_layer
44
+ self
45
+ end
46
+
47
+ def activation_function
48
+ unless @activation_function
49
+ layer = @layers.find { |l| l.activation_function }
50
+ @activation_function = layer.activation_function
51
+ end
52
+
53
+ @activation_function
54
+ end
55
+
56
+ def output_activation_function
57
+ unless @output_activation_function
58
+ layer = @layers.reverse.find { |l| l.activation_function }
59
+ @output_activation_function = layer.activation_function
60
+ end
61
+
62
+ @output_activation_function
63
+ end
64
+
65
+ def prep_input(input)
66
+ activation_function.prep_input(input)
67
+ end
68
+
69
+ def prep_output_target(target)
70
+ output_activation_function.prep_output_target(target)
71
+ end
72
+
73
+ def final_output(outputs)
74
+ outputs.last
75
+ end
76
+
77
+ def forward(input, hidden_state = nil, flattened = false, processed = false)
78
+ unless flattened || input.kind_of?(CooCoo::Vector)
79
+ input = CooCoo::Vector[input.to_a.flatten, num_inputs]
80
+ end
81
+
82
+ hidden_state ||= Hash.new
83
+
84
+ output = if processed
85
+ input
86
+ else
87
+ prep_input(input)
88
+ end
89
+
90
+ outputs = @layers.each_with_index.inject([]) do |acc, (layer, i)|
91
+ #debug("Layer: #{i} #{layer.num_inputs} #{layer.size}")
92
+ #debug("Input: #{input}")
93
+ #debug("Weights: #{layer.neurons[0].weights}")
94
+ output, hidden_state = layer.forward(output, hidden_state)
95
+ acc << output
96
+ #debug("Output: #{input}")
97
+ end
98
+
99
+ return outputs, hidden_state
100
+ end
101
+
102
+ def predict(input, hidden_state = nil, flattened = false, processed = false)
103
+ hidden_state ||= Hash.new
104
+ outputs, hidden_state = forward(input, hidden_state, flattened, processed)
105
+ out = final_output(outputs)
106
+ return out, hidden_state
107
+ end
108
+
109
+ def backprop(inputs, outputs, errors, hidden_state = nil)
110
+ hidden_state ||= Hash.new
111
+ d = @layers.reverse_each.each_with_index.inject([]) do |acc, (layer, i)|
112
+ input = if i < (@layers.size - 1)
113
+ outputs[@layers.size - i - 2]
114
+ else
115
+ prep_input(inputs) # TODO condition prep_input
116
+ end
117
+ #CooCoo.debug("#{self.class.name}.#{__method__}\t#{i} #{@layers.size - i - 1}\t#{input.size}\t#{outputs.size}")
118
+ deltas, hidden_state = layer.backprop(input,
119
+ outputs[@layers.size - i - 1],
120
+ errors,
121
+ hidden_state)
122
+ errors = layer.transfer_error(deltas)
123
+ acc.unshift(deltas)
124
+ end
125
+
126
+ return Sequence[d], hidden_state
127
+ end
128
+
129
+ def transfer_errors(deltas)
130
+ @layers.zip(deltas).collect do |layer, delta|
131
+ layer.transfer_error(delta)
132
+ end
133
+ end
134
+
135
+ def update_weights!(input, outputs, deltas)
136
+ adjust_weights!(weight_deltas(input, outputs, deltas))
137
+ self
138
+ end
139
+
140
+ def adjust_weights!(deltas)
141
+ @layers.each_with_index do |layer, i|
142
+ layer.adjust_weights!(deltas[i])
143
+ end
144
+
145
+ @age += 1
146
+ self
147
+ end
148
+
149
+ def weight_deltas(input, outputs, deltas)
150
+ d = @layers.each_with_index.collect do |layer, i|
151
+ inputs = if i != 0
152
+ outputs[i - 1]
153
+ else
154
+ prep_input(input)
155
+ end
156
+ layer.weight_deltas(inputs, deltas[i])
157
+ end
158
+
159
+ d
160
+ end
161
+
162
+ def learn(input, expecting, rate, cost_function = CostFunctions::MeanSquare, hidden_state = nil)
163
+ hidden_state ||= Hash.new
164
+ output, hidden_state = forward(input, hidden_state)
165
+ cost = cost_function.derivative(prep_input(expecting), output.last)
166
+ deltas, hidden_state = backprop(input, output, cost, hidden_state)
167
+ update_weights!(input, output, deltas * rate)
168
+ return self, hidden_state
169
+ rescue
170
+ CooCoo.debug("Network#learn caught #{$!}", input, expecting)
171
+ raise
172
+ end
173
+
174
+ def save(path)
175
+ File.write_to(path) do |f|
176
+ f.write(to_hash.to_yaml)
177
+ end
178
+ end
179
+
180
+ def load!(path)
181
+ yaml = YAML.load(File.read(path))
182
+ raise RuntimeError.new("Invalid YAML definition in #{path}") if yaml.nil?
183
+
184
+ update_from_hash!(yaml)
185
+
186
+ self
187
+ end
188
+
189
+ def update_from_hash!(h)
190
+ @layers = Array.new
191
+
192
+ h[:layers].each do |layer_hash|
193
+ @layers << CooCoo::LayerFactory.from_hash(layer_hash, self)
194
+ end
195
+
196
+ @age = h.fetch(:age, 0)
197
+ @command = h.fetch(:command, nil)
198
+ @comments = h.fetch(:comments) { Array.new }
199
+
200
+ self
201
+ end
202
+
203
+ def to_hash
204
+ { age: @age,
205
+ command: @command,
206
+ comments: @comments,
207
+ layers: @layers.collect { |l| l.to_hash(self) }
208
+ }
209
+ end
210
+
211
+ class << self
212
+ def from_a(layers)
213
+ self.new().update_from_a!(layers)
214
+ end
215
+
216
+ def from_hash(h)
217
+ self.new.update_from_hash!(h)
218
+ end
219
+
220
+ def load(path)
221
+ self.new().load!(path)
222
+ end
223
+ end
224
+ end
225
+ end
226
+
227
+ if __FILE__ == $0
228
+ SIZE = 10
229
+ net = CooCoo::Network.new()
230
+ net.layer(CooCoo::Layer.new(SIZE, SIZE / 2))
231
+ #net.layer(CooCoo::Layer.new(3, 3))
232
+ net.layer(CooCoo::Layer.new(SIZE / 2, SIZE / 2))
233
+ net.layer(CooCoo::Layer.new(SIZE / 2, 2))
234
+
235
+ inputs = 3.times.collect do |i|
236
+ CooCoo::Vector.zeros(SIZE)
237
+ end
238
+ inputs[0][0] = 1.0
239
+ inputs[1][2] = 1.0
240
+ inputs[2][3] = 1.0
241
+ targets = [ [ 1.0, 0.0 ],
242
+ [ 0.0, 1.0 ],
243
+ [ 0.0, 1.0 ]
244
+ ].collect do |v|
245
+ CooCoo::Vector[v]
246
+ end
247
+
248
+ ENV.fetch('LOOPS', 100).to_i.times do |i|
249
+ targets.zip(inputs).each do |target, input|
250
+ net.learn(input, target, 0.3)
251
+ end
252
+ end
253
+
254
+ inputs.each.zip(targets) do |input, target|
255
+ output, hidden_state = net.forward(input)
256
+ err = (net.prep_input(target) - output.last)
257
+ puts("#{input} -> #{target}\t#{err}")
258
+ output.each_with_index do |o, i|
259
+ puts("\tLayer #{i}:\t#{o}")
260
+ end
261
+ end
262
+
263
+ puts(net.to_hash)
264
+ end
@@ -0,0 +1,112 @@
1
+ require 'coo-coo/consts'
2
+ require 'coo-coo/debug'
3
+ require 'coo-coo/math'
4
+ require 'coo-coo/enum'
5
+ require 'coo-coo/activation_functions'
6
+
7
+ module CooCoo
8
+ class Neuron
9
+ def initialize(num_inputs, activation_func = CooCoo.default_activation)
10
+ @num_inputs = num_inputs
11
+ @activation_func = activation_func
12
+ @weights = @activation_func.initial_weights(num_inputs, 1)
13
+ @bias = @activation_func.initial_bias(1)[0]
14
+ end
15
+
16
+ def to_hash
17
+ { num_inputs: @num_inputs,
18
+ weights: @weights.to_a,
19
+ bias: @bias,
20
+ f: @activation_func.name
21
+ }
22
+ end
23
+
24
+ def update_from_hash!(h)
25
+ @num_inputs = h.fetch(:num_inputs, h.fetch(:weights, []).size)
26
+ @weights = CooCoo::Vector[h[:weights]]
27
+ @activation_func = CooCoo::ActivationFunctions.from_name(h[:f] || CooCoo.default_activation.name)
28
+ @bias = h.fetch(:bias, @activation_func.initial_bias(1)[0])
29
+ self
30
+ end
31
+
32
+ def self.from_hash(h)
33
+ self.new(h[:num_inputs] || h[:weights].size).update_from_hash!(h)
34
+ end
35
+
36
+ attr_reader :num_inputs
37
+ attr_reader :weights
38
+ attr_reader :bias
39
+
40
+ def forward(input)
41
+ transfer(activate(input))
42
+ end
43
+
44
+ def activate(input)
45
+ (@weights * input).sum + @bias
46
+ end
47
+
48
+ def transfer(activation)
49
+ @activation_func.call(activation)
50
+ end
51
+
52
+ def backprop(input, output, error)
53
+ # Properly: error * @activation_func.derivative(activate(input), output)
54
+ error * @activation_func.derivative(nil, output)
55
+ end
56
+
57
+ def transfer_error(delta)
58
+ @weights * delta
59
+ end
60
+
61
+ def weight_deltas(inputs, delta)
62
+ [ delta, inputs * delta ]
63
+ rescue
64
+ CooCoo.debug("#{$!}\n\t#{inputs.class}\t#{inputs}\n\t#{@weights.class}\t#{@weights}\n\t#{delta.class}\t#{delta}")
65
+ raise
66
+ end
67
+
68
+ def update_weights!(inputs, delta)
69
+ adjust_weights!(*weight_deltas(inputs, delta))
70
+ end
71
+
72
+ def adjust_weights!(bias_delta, weight_deltas)
73
+ @bias -= bias_delta
74
+ @weights -= weight_deltas
75
+ end
76
+
77
+ def ==(other)
78
+ if other.kind_of?(self.class)
79
+ num_inputs == other.num_inputs && @weights == other.weights
80
+ else
81
+ false
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ if __FILE__ == $0
88
+ require 'coo-coo/cost_functions'
89
+
90
+ n = CooCoo::Neuron.from_hash({ f: ENV.fetch("ACTIVATION", "Logistic"),
91
+ weights: [ 0.5, 0.5 ]
92
+ })
93
+ inputs = [ CooCoo::Vector[[ 0.25, 0.75 ]], CooCoo::Vector[[ 0.0, 1.0 ]] ]
94
+ targets = [ 0.0, 1.0 ]
95
+
96
+ ENV.fetch('LOOPS', 100).to_i.times do |i|
97
+ inputs.zip(targets).each do |input, target|
98
+ puts("#{i}: #{input} -> #{target}")
99
+ o = n.forward(input)
100
+ err1 = CooCoo::CostFunctions::MeanSquare.derivative(target, o)
101
+ puts("\tPre: #{input} * #{n.weights} = #{o}\t#{err1}\t#{CooCoo::CostFunctions::MeanSquare.call(target, o)}")
102
+ delta = n.backprop(input, o, err1)
103
+ puts("\tDelta: #{delta}")
104
+ n.update_weights!(input, delta * 0.3)
105
+ o = n.forward(input)
106
+ err2 = CooCoo::CostFunctions::MeanSquare.derivative(target, o)
107
+ puts("\tPost: #{input} * #{n.weights} = #{o}\t#{err2}")
108
+ puts("\tChange in Cost: #{err2} - #{err1} = #{err2 - err1}")
109
+ puts("")
110
+ end
111
+ end
112
+ end