ruby-dnn 1.1.4 → 1.2.2

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +2 -1
  4. data/README.md +39 -22
  5. data/examples/api-examples/early_stopping_example.rb +6 -6
  6. data/examples/api-examples/initializer_example.rb +6 -6
  7. data/examples/api-examples/regularizer_example.rb +6 -6
  8. data/examples/api-examples/save_example.rb +6 -6
  9. data/examples/dcgan/dcgan.rb +27 -27
  10. data/examples/judge-number/README.md +29 -0
  11. data/examples/judge-number/capture.PNG +0 -0
  12. data/examples/judge-number/convnet8.rb +70 -0
  13. data/examples/judge-number/make_weights.rb +5 -0
  14. data/examples/judge-number/mnist_predict.rb +20 -0
  15. data/examples/judge-number/mnist_train.rb +19 -0
  16. data/examples/judge-number/public/httpRequest.js +44 -0
  17. data/examples/judge-number/public/judgeNumber.js +61 -0
  18. data/examples/judge-number/server.rb +19 -0
  19. data/examples/judge-number/trained_mnist_params.marshal +0 -0
  20. data/examples/judge-number/views/index.erb +7 -0
  21. data/examples/mnist_conv2d_example.rb +3 -3
  22. data/examples/mnist_define_by_run.rb +7 -7
  23. data/examples/mnist_gpu.rb +47 -0
  24. data/examples/mnist_lstm_example.rb +1 -1
  25. data/examples/pix2pix/dcgan.rb +54 -66
  26. data/examples/pix2pix/train.rb +2 -2
  27. data/examples/vae.rb +13 -13
  28. data/img/cart-pole.gif +0 -0
  29. data/img/cycle-gan.PNG +0 -0
  30. data/img/facade-pix2pix.png +0 -0
  31. data/lib/dnn.rb +24 -3
  32. data/lib/dnn/core/callbacks.rb +6 -4
  33. data/lib/dnn/core/layers/basic_layers.rb +40 -22
  34. data/lib/dnn/core/layers/cnn_layers.rb +33 -5
  35. data/lib/dnn/core/layers/math_layers.rb +17 -9
  36. data/lib/dnn/core/layers/merge_layers.rb +2 -26
  37. data/lib/dnn/core/layers/split_layers.rb +39 -0
  38. data/lib/dnn/core/link.rb +14 -33
  39. data/lib/dnn/core/losses.rb +6 -12
  40. data/lib/dnn/core/models.rb +77 -10
  41. data/lib/dnn/core/optimizers.rb +8 -1
  42. data/lib/dnn/core/utils.rb +23 -0
  43. data/lib/dnn/image.rb +48 -0
  44. data/lib/dnn/version.rb +1 -1
  45. data/ruby-dnn.gemspec +2 -15
  46. metadata +40 -20
  47. data/bin/console +0 -14
  48. data/bin/setup +0 -8
@@ -23,8 +23,8 @@ epochs = 20
23
23
  batch_size = 128
24
24
 
25
25
  if initial_epoch == 1
26
- gen = Generator.new([32, 32, 1])
27
- dis = Discriminator.new([32, 32, 1], [32, 32, 3])
26
+ gen = Generator.new([32, 32, 1], 32)
27
+ dis = Discriminator.new([32, 32, 1], [32, 32, 3], 32)
28
28
  dcgan = DCGAN.new(gen, dis)
29
29
  gen.setup(Adam.new(alpha: 0.0002, beta1: 0.5), MeanAbsoluteError.new)
30
30
  dis.setup(Adam.new(alpha: 0.00001, beta1: 0.1), SigmoidCrossEntropy.new)
@@ -28,24 +28,24 @@ end
28
28
  class Encoder < Model
29
29
  def initialize
30
30
  super
31
- @l1 = Dense.new(196)
32
- @l2 = Dense.new(49)
33
- @l3_1 = Dense.new($z_dim)
34
- @l3_2 = Dense.new($z_dim)
31
+ @d1 = Dense.new(196)
32
+ @d2 = Dense.new(49)
33
+ @d3_1 = Dense.new($z_dim)
34
+ @d3_2 = Dense.new($z_dim)
35
35
  @bn1 = BatchNormalization.new
36
36
  @bn2 = BatchNormalization.new
37
37
  end
38
38
 
39
39
  def forward(x)
40
40
  x = InputLayer.new(784).(x)
41
- x = @l1.(x)
41
+ x = @d1.(x)
42
42
  x = @bn1.(x)
43
43
  x = ReLU.(x)
44
- x = @l2.(x)
44
+ x = @d2.(x)
45
45
  x = @bn2.(x)
46
46
  x = ReLU.(x)
47
- z_mean = @l3_1.(x)
48
- z_sigma = @l3_2.(x)
47
+ z_mean = @d3_1.(x)
48
+ z_sigma = @d3_2.(x)
49
49
  [z_mean, z_sigma]
50
50
  end
51
51
  end
@@ -53,16 +53,16 @@ end
53
53
  class Decoder < Model
54
54
  def initialize
55
55
  super
56
- @l3 = Dense.new(196)
57
- @l4 = Dense.new(784)
56
+ @d1 = Dense.new(196)
57
+ @d2 = Dense.new(784)
58
58
  @bn1 = BatchNormalization.new
59
59
  end
60
60
 
61
61
  def forward(z)
62
- x = @l3.(z)
62
+ x = @d1.(z)
63
63
  x = @bn1.(x)
64
64
  x = ReLU.(x)
65
- x = @l4.(x)
65
+ x = @d2.(x)
66
66
  x
67
67
  end
68
68
  end
@@ -97,7 +97,7 @@ model = VAE.new
97
97
  dec = model.dec
98
98
  model.setup(Adam.new, VAELoss.new)
99
99
 
100
- model.train(x_train, x_train, 10, batch_size: 100)
100
+ model.train(x_train, x_train, 10, batch_size: 128)
101
101
 
102
102
  images = []
103
103
  10.times do |i|
Binary file
Binary file
Binary file
data/lib/dnn.rb CHANGED
@@ -1,9 +1,29 @@
1
+ require "numo/narray"
2
+
1
3
  module DNN
2
- if defined? ::Cumo
4
+ if ENV["RUBY_DNN_USE_CUMO"] == "ENABLE"
5
+ require "cumo/narray"
3
6
  Xumo = ::Cumo
4
7
  else
5
- require "numo/narray"
6
- Xumo = ::Numo
8
+ if defined? ::Cumo
9
+ Xumo = ::Cumo
10
+ else
11
+ Xumo = ::Numo
12
+ end
13
+ end
14
+
15
+ def self.use_cumo?
16
+ defined? ::Cumo
17
+ end
18
+
19
+ def self.cudnn_available?
20
+ return false unless defined? ::Cumo
21
+ Cumo::CUDA::CUDNN.available?
22
+ end
23
+
24
+ def self.use_cudnn?
25
+ return false unless ENV["RUBY_DNN_USE_CUDNN"] == "ENABLE"
26
+ cudnn_available?
7
27
  end
8
28
  end
9
29
 
@@ -20,6 +40,7 @@ require_relative "dnn/core/layers/basic_layers"
20
40
  require_relative "dnn/core/layers/normalizations"
21
41
  require_relative "dnn/core/layers/activations"
22
42
  require_relative "dnn/core/layers/merge_layers"
43
+ require_relative "dnn/core/layers/split_layers"
23
44
  require_relative "dnn/core/layers/cnn_layers"
24
45
  require_relative "dnn/core/layers/embedding"
25
46
  require_relative "dnn/core/layers/rnn_layers"
@@ -104,6 +104,7 @@ module DNN
104
104
  # A callback that save the log.
105
105
  # The following logs will be recorded.
106
106
  # epoch: Current epoch.
107
+ # step: Current step in epoch.
107
108
  # train_loss: Batch training loss.
108
109
  # test_loss: Mean test loss.
109
110
  # test_accuracy: Test accuracy.
@@ -111,6 +112,7 @@ module DNN
111
112
  def initialize
112
113
  @log = {
113
114
  epoch: [],
115
+ step: [],
114
116
  train_loss: [],
115
117
  test_loss: [],
116
118
  test_accuracy: [],
@@ -122,7 +124,7 @@ module DNN
122
124
  end
123
125
 
124
126
  def after_train_on_batch
125
- logging(:train_loss)
127
+ logging(:train_loss, :step)
126
128
  end
127
129
 
128
130
  # Get a log.
@@ -130,10 +132,10 @@ module DNN
130
132
  # @return [Numo::NArray] Return the recorded log.
131
133
  def get_log(tag)
132
134
  case tag
133
- when :epoch
134
- Numo::UInt32.cast(@log[tag])
135
+ when :epoch, :step
136
+ Xumo::UInt32.cast(@log[tag])
135
137
  else
136
- Numo::SFloat.cast(@log[tag])
138
+ Xumo::SFloat.cast(@log[tag])
137
139
  end
138
140
  end
139
141
 
@@ -2,20 +2,21 @@ module DNN
2
2
  module Layers
3
3
 
4
4
  module LayerNode
5
- def forward(input)
6
- x = input.data
7
- prev = (input.is_a?(Tensor) ? input.link : input)
8
- y = forward_node(x)
9
- link = Link.new(prev, self)
10
- prev.next = link if prev.is_a?(Link)
11
- Tensor.convert(y, link)
5
+ def forward(*inputs)
6
+ xs = inputs.map(&:data)
7
+ prevs = inputs.map { |input| input.is_a?(Tensor) ? input.link : input }
8
+ ys = forward_node(*xs)
9
+ num_outputs = (ys.is_a?(Array) ? ys.length : 1)
10
+ link = Link.new(prevs: prevs, layer_node: self, num_outputs: num_outputs)
11
+ prevs.map { |prev| prev.next = link if prev.is_a?(Link) }
12
+ Tensor.convert(ys, link)
12
13
  end
13
14
 
14
- def forward_node(x)
15
+ def forward_node(*xs)
15
16
  raise NotImplementedError, "Class '#{self.class.name}' has implement method 'forward_node'"
16
17
  end
17
18
 
18
- def backward_node(dy)
19
+ def backward_node(*dys)
19
20
  raise NotImplementedError, "Class '#{self.class.name}' has implement method 'backward_node'"
20
21
  end
21
22
  end
@@ -292,14 +293,8 @@ module DNN
292
293
  end
293
294
 
294
295
  class Flatten < Layer
295
- include LayerNode
296
-
297
- def forward_node(x)
298
- x.reshape(x.shape[0], *@output_shape)
299
- end
300
-
301
- def backward_node(dy)
302
- dy.reshape(dy.shape[0], *@input_shape)
296
+ def forward(x)
297
+ Reshape.(x, @output_shape)
303
298
  end
304
299
 
305
300
  def compute_output_shape
@@ -320,13 +315,37 @@ module DNN
320
315
  end
321
316
 
322
317
  def forward_node(x)
323
- x.reshape(x.shape[0], *@output_shape)
318
+ if DNN.use_cumo?
319
+ _forward_gpu(x)
320
+ else
321
+ _forward_cpu(x)
322
+ end
324
323
  end
325
324
 
326
325
  def backward_node(dy)
326
+ if DNN.use_cumo?
327
+ _backward_gpu(dy)
328
+ else
329
+ _backward_cpu(dy)
330
+ end
331
+ end
332
+
333
+ def _forward_cpu(x)
334
+ x.reshape(x.shape[0], *@output_shape)
335
+ end
336
+
337
+ def _backward_cpu(dy)
327
338
  dy.reshape(dy.shape[0], *@input_shape)
328
339
  end
329
340
 
341
+ def _forward_gpu(x)
342
+ x.flatten.reshape(x.shape[0], *@output_shape)
343
+ end
344
+
345
+ def _backward_gpu(dy)
346
+ dy.flatten.reshape(dy.shape[0], *@input_shape)
347
+ end
348
+
330
349
  def to_hash
331
350
  super(shape: @shape)
332
351
  end
@@ -417,8 +436,8 @@ module DNN
417
436
  def forward_node(x)
418
437
  if DNN.learning_phase
419
438
  Xumo::SFloat.srand(@rnd.rand(1 << 31))
420
- @mask = Xumo::SFloat.new(*x.shape).rand < @dropout_ratio
421
- x[@mask] = 0
439
+ @mask = Xumo::SFloat.cast(Xumo::SFloat.new(*x.shape).rand >= @dropout_ratio)
440
+ x = x * @mask
422
441
  elsif @use_scale
423
442
  x *= (1 - @dropout_ratio)
424
443
  end
@@ -426,8 +445,7 @@ module DNN
426
445
  end
427
446
 
428
447
  def backward_node(dy)
429
- dy[@mask] = 0
430
- dy
448
+ dy * @mask
431
449
  end
432
450
 
433
451
  def to_hash
@@ -6,10 +6,27 @@ module DNN
6
6
  module_function
7
7
 
8
8
  # img[bsize, out_h, out_w, ch] to col[bsize * out_h * out_w, fil_h * fil_w * ch]
9
- def im2col(img, out_h, out_w, fil_h, fil_w, strides)
9
+ def im2col(*args)
10
+ if DNN.use_cumo?
11
+ im2col_gpu(*args)
12
+ else
13
+ im2col_cpu(*args)
14
+ end
15
+ end
16
+
17
+ # col[bsize * out_h * out_w, fil_h * fil_w * ch] to img[bsize, out_h, out_w, ch]
18
+ def col2im(*args)
19
+ if DNN.use_cumo?
20
+ col2im_gpu(*args)
21
+ else
22
+ col2im_cpu(*args)
23
+ end
24
+ end
25
+
26
+ def im2col_cpu(img, out_h, out_w, fil_h, fil_w, strides)
10
27
  bsize = img.shape[0]
11
28
  ch = img.shape[3]
12
- col = Xumo::SFloat.zeros(bsize, out_h, out_w, fil_h, fil_w, ch)
29
+ col = img.class.zeros(bsize, out_h, out_w, fil_h, fil_w, ch)
13
30
  (0...fil_h).each do |i|
14
31
  i_range = (i...(i + strides[0] * out_h)).step(strides[0]).to_a
15
32
  (0...fil_w).each do |j|
@@ -20,11 +37,16 @@ module DNN
20
37
  col.reshape(bsize * out_h * out_w, fil_h * fil_w * ch)
21
38
  end
22
39
 
23
- # col[bsize * out_h * out_w, fil_h * fil_w * ch] to img[bsize, out_h, out_w, ch]
24
- def col2im(col, img_shape, out_h, out_w, fil_h, fil_w, strides)
40
+ def im2col_gpu(img, out_h, out_w, fil_h, fil_w, strides)
41
+ img = Utils.cumo2numo(img)
42
+ col = im2col_cpu(img, out_h, out_w, fil_h, fil_w, strides)
43
+ Utils.numo2cumo(col)
44
+ end
45
+
46
+ def col2im_cpu(col, img_shape, out_h, out_w, fil_h, fil_w, strides)
25
47
  bsize, img_h, img_w, ch = img_shape
26
48
  col = col.reshape(bsize, out_h, out_w, fil_h, fil_w, ch)
27
- img = Xumo::SFloat.zeros(bsize, img_h, img_w, ch)
49
+ img = col.class.zeros(bsize, img_h, img_w, ch)
28
50
  (0...fil_h).each do |i|
29
51
  i_range = (i...(i + strides[0] * out_h)).step(strides[0]).to_a
30
52
  (0...fil_w).each do |j|
@@ -35,6 +57,12 @@ module DNN
35
57
  img
36
58
  end
37
59
 
60
+ def col2im_gpu(col, img_shape, out_h, out_w, fil_h, fil_w, strides)
61
+ col = Utils.cumo2numo(col)
62
+ img = col2im_cpu(col, img_shape, out_h, out_w, fil_h, fil_w, strides)
63
+ Utils.numo2cumo(img)
64
+ end
65
+
38
66
  def zero_padding(img, pad)
39
67
  bsize, img_h, img_w, ch = img.shape
40
68
  img2 = Xumo::SFloat.zeros(bsize, img_h + pad[0], img_w + pad[1], ch)
@@ -61,7 +61,7 @@ module DNN
61
61
  end
62
62
 
63
63
  class Add < MergeLayer
64
- include MergeLayerNode
64
+ include LayerNode
65
65
 
66
66
  def forward_node(x1, x2)
67
67
  @x1_shape = x1.shape
@@ -77,7 +77,7 @@ module DNN
77
77
  end
78
78
 
79
79
  class Sub < MergeLayer
80
- include MergeLayerNode
80
+ include LayerNode
81
81
 
82
82
  def forward_node(x1, x2)
83
83
  @x1_shape = x1.shape
@@ -93,7 +93,7 @@ module DNN
93
93
  end
94
94
 
95
95
  class Mul < MergeLayer
96
- include MergeLayerNode
96
+ include LayerNode
97
97
 
98
98
  def forward_node(x1, x2)
99
99
  @x1, @x2 = x1, x2
@@ -108,7 +108,7 @@ module DNN
108
108
  end
109
109
 
110
110
  class Div < MergeLayer
111
- include MergeLayerNode
111
+ include LayerNode
112
112
 
113
113
  def forward_node(x1, x2)
114
114
  @x1, @x2 = x1, x2
@@ -123,7 +123,7 @@ module DNN
123
123
  end
124
124
 
125
125
  class Dot < MergeLayer
126
- include MergeLayerNode
126
+ include LayerNode
127
127
 
128
128
  def forward_node(x1, x2)
129
129
  @x1, @x2 = x1, x2
@@ -205,8 +205,11 @@ module DNN
205
205
 
206
206
  def forward_node(x)
207
207
  @x_shape = x.shape
208
- @dim = x.shape[@axis]
209
- x.sum(axis: @axis, keepdims: true)
208
+ if @axis
209
+ x.sum(axis: @axis, keepdims: true)
210
+ else
211
+ x.sum
212
+ end
210
213
  end
211
214
 
212
215
  def backward_node(dy)
@@ -236,8 +239,13 @@ module DNN
236
239
 
237
240
  def forward_node(x)
238
241
  @x_shape = x.shape
239
- @dim = x.shape[@axis]
240
- x.mean(axis: @axis, keepdims: true)
242
+ if @axis
243
+ @dim = x.shape[@axis]
244
+ x.mean(axis: @axis, keepdims: true)
245
+ else
246
+ @dim = x.size
247
+ x.mean
248
+ end
241
249
  end
242
250
 
243
251
  def backward_node(dy)
@@ -1,30 +1,6 @@
1
1
  module DNN
2
2
  module Layers
3
3
 
4
- module MergeLayerNode
5
- def forward(input1, input2)
6
- x1 = input1.data
7
- x2 = input2.data
8
- prev1 = (input1.is_a?(Tensor) ? input1.link : input1)
9
- prev2 = (input2.is_a?(Tensor) ? input2.link : input2)
10
- y = forward_node(x1, x2)
11
- link = TwoInputLink.new(prev1, prev2, self)
12
- Tensor.convert(y, link)
13
- end
14
-
15
- def backward(dy)
16
- backward_node(dy)
17
- end
18
-
19
- def forward_node(x1, x2)
20
- raise NotImplementedError, "Class '#{self.class.name}' has implement method 'forward_node'"
21
- end
22
-
23
- def backward_node(dy)
24
- raise NotImplementedError, "Class '#{self.class.name}' has implement method 'backward_node'"
25
- end
26
- end
27
-
28
4
  class MergeLayer < Layer
29
5
  def self.call(x1, x2, *args)
30
6
  new(*args).call(x1, x2)
@@ -33,7 +9,7 @@ module DNN
33
9
  def call(input1, input2)
34
10
  input1 = Tensor.convert(input1) if !input1.is_a?(Tensor) && !input1.is_a?(Param)
35
11
  input2 = Tensor.convert(input2) if !input2.is_a?(Tensor) && !input2.is_a?(Param)
36
- if input1.data.is_a?(Numo::NArray)
12
+ if input1.data.is_a?(Xumo::NArray)
37
13
  build(input1.data.shape[1..-1]) unless built?
38
14
  else
39
15
  build([1]) unless built?
@@ -43,7 +19,7 @@ module DNN
43
19
  end
44
20
 
45
21
  class Concatenate < MergeLayer
46
- include MergeLayerNode
22
+ include LayerNode
47
23
 
48
24
  attr_reader :axis
49
25