ruby-dnn 0.15.3 → 0.16.0
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.
- checksums.yaml +4 -4
- data/Rakefile +1 -9
- data/examples/api-examples/early_stopping_example.rb +1 -1
- data/examples/api-examples/initializer_example.rb +1 -1
- data/examples/api-examples/regularizer_example.rb +1 -1
- data/examples/api-examples/save_example.rb +1 -1
- data/examples/dcgan/dcgan.rb +3 -3
- data/examples/iris_example.rb +41 -17
- data/examples/mnist_define_by_run.rb +1 -1
- data/examples/pix2pix/dcgan.rb +157 -0
- data/examples/pix2pix/imgen.rb +27 -0
- data/examples/pix2pix/train.rb +52 -0
- data/lib/dnn.rb +2 -0
- data/lib/dnn/core/layers/activations.rb +37 -19
- data/lib/dnn/core/layers/basic_layers.rb +110 -25
- data/lib/dnn/core/layers/cnn_layers.rb +19 -21
- data/lib/dnn/core/layers/embedding.rb +3 -3
- data/lib/dnn/core/layers/math_layers.rb +169 -0
- data/lib/dnn/core/layers/merge_layers.rb +29 -24
- data/lib/dnn/core/layers/normalizations.rb +4 -2
- data/lib/dnn/core/layers/rnn_layers.rb +44 -36
- data/lib/dnn/core/link.rb +7 -2
- data/lib/dnn/core/losses.rb +54 -30
- data/lib/dnn/core/models.rb +47 -47
- data/lib/dnn/core/monkey_patch.rb +75 -0
- data/lib/dnn/core/optimizers.rb +10 -6
- data/lib/dnn/core/param.rb +17 -0
- data/lib/dnn/core/regularizers.rb +35 -33
- data/lib/dnn/core/tensor.rb +40 -0
- data/lib/dnn/core/utils.rb +1 -1
- data/lib/dnn/datasets/cifar10.rb +10 -9
- data/lib/dnn/datasets/cifar100.rb +10 -9
- data/lib/dnn/datasets/downloader.rb +1 -5
- data/lib/dnn/datasets/fashion-mnist.rb +4 -12
- data/lib/dnn/datasets/iris.rb +9 -9
- data/lib/dnn/datasets/mnist.rb +4 -12
- data/lib/dnn/datasets/stl-10.rb +6 -8
- data/lib/dnn/version.rb +1 -1
- data/ruby-dnn.gemspec +1 -1
- metadata +7 -5
- data/ext/cifar_loader/cifar_loader.c +0 -77
- data/ext/cifar_loader/extconf.rb +0 -3
data/lib/dnn/core/models.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
module DNN
|
2
2
|
module Models
|
3
3
|
|
4
|
+
# This class is used to hold multiple layers in an array.
|
4
5
|
class LayersList < Array
|
5
6
|
def self.from_hash_list(hash_list)
|
6
7
|
layers_list = new
|
@@ -19,7 +20,7 @@ module DNN
|
|
19
20
|
end
|
20
21
|
|
21
22
|
def to_hash_list
|
22
|
-
map
|
23
|
+
map(&:to_hash)
|
23
24
|
end
|
24
25
|
|
25
26
|
# Get the all layers.
|
@@ -38,8 +39,18 @@ module DNN
|
|
38
39
|
end
|
39
40
|
|
40
41
|
class Chain
|
41
|
-
|
42
|
-
|
42
|
+
# Forward propagation.
|
43
|
+
# @param [Tensor] input_tensor Input tensor.
|
44
|
+
# @return [Tensor] Output tensor.
|
45
|
+
def forward(input_tensor)
|
46
|
+
raise NotImplementedError, "Class '#{self.class.name}' has implement method 'forward'"
|
47
|
+
end
|
48
|
+
|
49
|
+
# Forward propagation and create a link.
|
50
|
+
# @param [Tensor] input_tensor Input tensor.
|
51
|
+
# @return [Tensor] Output tensor.
|
52
|
+
def call(input_tensor)
|
53
|
+
forward(input_tensor)
|
43
54
|
end
|
44
55
|
|
45
56
|
# Get the all layers.
|
@@ -109,13 +120,19 @@ module DNN
|
|
109
120
|
def initialize
|
110
121
|
@optimizer = nil
|
111
122
|
@loss_func = nil
|
112
|
-
@last_link = nil
|
113
123
|
@built = false
|
114
124
|
@callbacks = []
|
115
125
|
@layers_cache = nil
|
116
126
|
@last_log = {}
|
117
127
|
end
|
118
128
|
|
129
|
+
def call(inputs)
|
130
|
+
@layers_cache = nil
|
131
|
+
output_tensor = forward(inputs)
|
132
|
+
@built = true unless @built
|
133
|
+
output_tensor
|
134
|
+
end
|
135
|
+
|
119
136
|
# Set optimizer and loss_func to model.
|
120
137
|
# @param [DNN::Optimizers::Optimizer] optimizer Optimizer to use for learning.
|
121
138
|
# @param [DNN::Losses::Loss] loss_func Loss function to use for learning.
|
@@ -204,10 +221,10 @@ module DNN
|
|
204
221
|
|
205
222
|
if test
|
206
223
|
acc, loss = if test.is_a?(Array)
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
224
|
+
evaluate(test[0], test[1], batch_size: batch_size)
|
225
|
+
else
|
226
|
+
evaluate_by_iterator(test, batch_size: batch_size)
|
227
|
+
end
|
211
228
|
print " " + metrics_to_str({ accuracy: acc, test_loss: loss }) if verbose
|
212
229
|
end
|
213
230
|
puts "" if verbose
|
@@ -242,15 +259,14 @@ module DNN
|
|
242
259
|
raise DNN_Error, "The model is not loss_func setup complete." unless @loss_func
|
243
260
|
check_xy_type(x, y)
|
244
261
|
call_callbacks(:before_train_on_batch)
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
backward(
|
249
|
-
@optimizer.update(
|
250
|
-
@
|
251
|
-
@last_log[:train_loss] = loss_value
|
262
|
+
DNN.learning_phase = true
|
263
|
+
out = call(Tensor.convert(x))
|
264
|
+
loss = @loss_func.loss(out, Tensor.convert(y), layers)
|
265
|
+
loss.link.backward(Xumo::SFloat.zeros(y[0...1, false].shape))
|
266
|
+
@optimizer.update(get_all_trainable_params)
|
267
|
+
@last_log[:train_loss] = loss.data
|
252
268
|
call_callbacks(:after_train_on_batch)
|
253
|
-
|
269
|
+
loss.data
|
254
270
|
end
|
255
271
|
|
256
272
|
# Evaluate model and get accuracy and loss of test data.
|
@@ -291,11 +307,12 @@ module DNN
|
|
291
307
|
# @return [Array] Returns the test data accuracy and mean loss in the form [accuracy, mean_loss].
|
292
308
|
def test_on_batch(x, y)
|
293
309
|
call_callbacks(:before_test_on_batch)
|
294
|
-
|
295
|
-
|
296
|
-
|
310
|
+
DNN.learning_phase = false
|
311
|
+
out = call(Tensor.convert(x))
|
312
|
+
correct = accuracy(out.data, y)
|
313
|
+
loss = @loss_func.(out, Tensor.convert(y))
|
297
314
|
call_callbacks(:after_test_on_batch)
|
298
|
-
[correct,
|
315
|
+
[correct, loss.data]
|
299
316
|
end
|
300
317
|
|
301
318
|
# Implement the process to accuracy this model.
|
@@ -323,7 +340,9 @@ module DNN
|
|
323
340
|
# @param [Boolean] use_loss_activation Use loss activation when loss has an activation.
|
324
341
|
def predict(x, use_loss_activation: true)
|
325
342
|
check_xy_type(x)
|
326
|
-
|
343
|
+
DNN.learning_phase = false
|
344
|
+
out = call(Tensor.convert(x))
|
345
|
+
y = out.data
|
327
346
|
if use_loss_activation && @loss_func.class.respond_to?(:activation)
|
328
347
|
y = @loss_func.class.activation(y)
|
329
348
|
end
|
@@ -386,9 +405,7 @@ module DNN
|
|
386
405
|
# @return [DNN::Layers::Layer] Return the layer.
|
387
406
|
def get_layer(name)
|
388
407
|
layer = instance_variable_get("@#{name}")
|
389
|
-
if layer.is_a?(Layers::Layer) || layer.is_a?(Chain) || layer.is_a?(LayersList)
|
390
|
-
return layer
|
391
|
-
end
|
408
|
+
return layer if layer.is_a?(Layers::Layer) || layer.is_a?(Chain) || layer.is_a?(LayersList)
|
392
409
|
nil
|
393
410
|
end
|
394
411
|
|
@@ -398,11 +415,8 @@ module DNN
|
|
398
415
|
end
|
399
416
|
|
400
417
|
def clean_layers
|
401
|
-
layers.each
|
402
|
-
layer.clean
|
403
|
-
end
|
418
|
+
layers.each(&:clean)
|
404
419
|
@loss_func.clean
|
405
|
-
@last_link = nil
|
406
420
|
@layers_cache = nil
|
407
421
|
end
|
408
422
|
|
@@ -424,24 +438,10 @@ module DNN
|
|
424
438
|
|
425
439
|
private
|
426
440
|
|
427
|
-
def
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
x.map { |a| Tensor.new(a, nil) }
|
432
|
-
else
|
433
|
-
Tensor.new(x, nil)
|
434
|
-
end
|
435
|
-
output_tensor = call(inputs)
|
436
|
-
@last_link = output_tensor.link
|
437
|
-
unless @built
|
438
|
-
@built = true
|
439
|
-
end
|
440
|
-
output_tensor.data
|
441
|
-
end
|
442
|
-
|
443
|
-
def backward(dy)
|
444
|
-
@last_link.backward(dy)
|
441
|
+
def get_all_trainable_params
|
442
|
+
layers.select { |layer| layer.is_a?(Layers::TrainableLayer) && layer.trainable }
|
443
|
+
.map { |layer| layer.get_params.values }.flatten.compact
|
444
|
+
.select(&:grad)
|
445
445
|
end
|
446
446
|
|
447
447
|
def call_callbacks(event)
|
@@ -512,7 +512,7 @@ module DNN
|
|
512
512
|
@stack.delete(layer) ? true : false
|
513
513
|
end
|
514
514
|
|
515
|
-
def
|
515
|
+
def forward(x)
|
516
516
|
@stack.each do |layer|
|
517
517
|
x = layer.(x)
|
518
518
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
class Integer
|
2
|
+
alias dnn__add +
|
3
|
+
def +(other)
|
4
|
+
if other.is_a?(DNN::Tensor)
|
5
|
+
DNN::Layers::Add.(self, other)
|
6
|
+
else
|
7
|
+
dnn__add(other)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
alias dnn__sub -
|
12
|
+
def -(other)
|
13
|
+
if other.is_a?(DNN::Tensor)
|
14
|
+
DNN::Layers::Sub.(self, other)
|
15
|
+
else
|
16
|
+
dnn__sub(other)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
alias dnn__mul *
|
21
|
+
def *(other)
|
22
|
+
if other.is_a?(DNN::Tensor)
|
23
|
+
DNN::Layers::Mul.(self, other)
|
24
|
+
else
|
25
|
+
dnn__mul(other)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
alias dnn__div /
|
30
|
+
def /(other)
|
31
|
+
if other.is_a?(DNN::Tensor)
|
32
|
+
DNN::Layers::Div.(self, other)
|
33
|
+
else
|
34
|
+
dnn__div(other)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class Float
|
40
|
+
alias dnn__add +
|
41
|
+
def +(other)
|
42
|
+
if other.is_a?(DNN::Tensor)
|
43
|
+
DNN::Layers::Add.(self, other)
|
44
|
+
else
|
45
|
+
dnn__add(other)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
alias dnn__sub -
|
50
|
+
def -(other)
|
51
|
+
if other.is_a?(DNN::Tensor)
|
52
|
+
DNN::Layers::Sub.(self, other)
|
53
|
+
else
|
54
|
+
dnn__sub(other)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
alias dnn__mul *
|
59
|
+
def *(other)
|
60
|
+
if other.is_a?(DNN::Tensor)
|
61
|
+
DNN::Layers::Mul.(self, other)
|
62
|
+
else
|
63
|
+
dnn__mul(other)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
alias dnn__div /
|
68
|
+
def /(other)
|
69
|
+
if other.is_a?(DNN::Tensor)
|
70
|
+
DNN::Layers::Div.(self, other)
|
71
|
+
else
|
72
|
+
dnn__div(other)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/dnn/core/optimizers.rb
CHANGED
@@ -19,16 +19,20 @@ module DNN
|
|
19
19
|
@clip_norm = clip_norm
|
20
20
|
end
|
21
21
|
|
22
|
+
def update(params)
|
23
|
+
clip_grads(params) if @clip_norm
|
24
|
+
update_params(params)
|
25
|
+
params.each do |param|
|
26
|
+
param.grad = Xumo::SFloat[0]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
22
30
|
# Update layers has params.
|
23
|
-
def
|
31
|
+
def update_layers(layers)
|
24
32
|
target_params = layers.select { |layer| layer.is_a?(Layers::TrainableLayer) && layer.trainable }
|
25
33
|
.map { |layer| layer.get_params.values }.flatten.compact
|
26
34
|
.select(&:grad)
|
27
|
-
|
28
|
-
update_params(target_params)
|
29
|
-
target_params.each do |param|
|
30
|
-
param.grad = Xumo::SFloat[0]
|
31
|
-
end
|
35
|
+
update(target_params)
|
32
36
|
end
|
33
37
|
|
34
38
|
def to_hash(merge_hash = nil)
|
data/lib/dnn/core/param.rb
CHANGED
@@ -1,11 +1,28 @@
|
|
1
1
|
module DNN
|
2
2
|
class Param
|
3
|
+
attr_accessor :trainable
|
3
4
|
attr_accessor :data
|
4
5
|
attr_accessor :grad
|
5
6
|
|
6
7
|
def initialize(data = nil, grad = nil)
|
7
8
|
@data = data
|
8
9
|
@grad = grad
|
10
|
+
@trainable = true
|
11
|
+
end
|
12
|
+
|
13
|
+
def backward(grad)
|
14
|
+
if @trainable
|
15
|
+
@grad ||= Xumo::SFloat[0]
|
16
|
+
if @data.shape == grad.shape
|
17
|
+
@grad += grad
|
18
|
+
elsif @data.shape == grad.shape[1..-1]
|
19
|
+
@grad += grad.sum(0)
|
20
|
+
else
|
21
|
+
raise DNN_Error, "Shape is missmatch."
|
22
|
+
end
|
23
|
+
else
|
24
|
+
@grad = Xumo::SFloat[0]
|
25
|
+
end
|
9
26
|
end
|
10
27
|
end
|
11
28
|
end
|
@@ -17,10 +17,6 @@ module DNN
|
|
17
17
|
raise NotImplementedError, "Class '#{self.class.name}' has implement method 'forward'"
|
18
18
|
end
|
19
19
|
|
20
|
-
def backward
|
21
|
-
raise NotImplementedError, "Class '#{self.class.name}' has implement method 'backward'"
|
22
|
-
end
|
23
|
-
|
24
20
|
def to_hash(merge_hash)
|
25
21
|
hash = { class: self.class.name }
|
26
22
|
hash.merge!(merge_hash)
|
@@ -33,25 +29,25 @@ module DNN
|
|
33
29
|
end
|
34
30
|
|
35
31
|
class L1 < Regularizer
|
36
|
-
attr_accessor :l1_lambda
|
37
|
-
|
38
32
|
# @param [Float] l1_lambda L1 regularizer coefficient.
|
39
33
|
def initialize(l1_lambda = 0.01)
|
40
|
-
@
|
34
|
+
@l1 = Layers::Lasso.new(l1_lambda)
|
41
35
|
end
|
42
36
|
|
43
37
|
def forward(x)
|
44
|
-
x + @
|
38
|
+
x + @l1.(@param)
|
39
|
+
end
|
40
|
+
|
41
|
+
def l1_lambda
|
42
|
+
@l1.l1_lambda
|
45
43
|
end
|
46
44
|
|
47
|
-
def
|
48
|
-
|
49
|
-
dparam[@param.data < 0] = -1
|
50
|
-
@param.grad += @l1_lambda * dparam
|
45
|
+
def l1_lambda=(lam)
|
46
|
+
@l1.l1_lambda = lam
|
51
47
|
end
|
52
48
|
|
53
49
|
def to_hash
|
54
|
-
super(l1_lambda:
|
50
|
+
super(l1_lambda: l1_lambda)
|
55
51
|
end
|
56
52
|
|
57
53
|
def load_hash(hash)
|
@@ -60,23 +56,25 @@ module DNN
|
|
60
56
|
end
|
61
57
|
|
62
58
|
class L2 < Regularizer
|
63
|
-
attr_accessor :l2_lambda
|
64
|
-
|
65
59
|
# @param [Float] l2_lambda L2 regularizer coefficient.
|
66
60
|
def initialize(l2_lambda = 0.01)
|
67
|
-
@
|
61
|
+
@l2 = Layers::Ridge.new(l2_lambda)
|
68
62
|
end
|
69
63
|
|
70
64
|
def forward(x)
|
71
|
-
x +
|
65
|
+
x + @l2.(@param)
|
66
|
+
end
|
67
|
+
|
68
|
+
def l2_lambda
|
69
|
+
@l2.l2_lambda
|
72
70
|
end
|
73
71
|
|
74
|
-
def
|
75
|
-
@
|
72
|
+
def l2_lambda=(lam)
|
73
|
+
@l2.l2_lambda = lam
|
76
74
|
end
|
77
75
|
|
78
76
|
def to_hash
|
79
|
-
super(l2_lambda:
|
77
|
+
super(l2_lambda: l2_lambda)
|
80
78
|
end
|
81
79
|
|
82
80
|
def load_hash(hash)
|
@@ -85,27 +83,31 @@ module DNN
|
|
85
83
|
end
|
86
84
|
|
87
85
|
class L1L2 < Regularizer
|
88
|
-
attr_accessor :l1_lambda
|
89
|
-
attr_accessor :l2_lambda
|
90
|
-
|
91
86
|
# @param [Float] l1_lambda L1 regularizer coefficient.
|
92
87
|
# @param [Float] l2_lambda L2 regularizer coefficient.
|
93
88
|
def initialize(l1_lambda = 0.01, l2_lambda = 0.01)
|
94
|
-
@
|
95
|
-
@
|
89
|
+
@l1 = Layers::Lasso.new(l1_lambda)
|
90
|
+
@l2 = Layers::Ridge.new(l2_lambda)
|
96
91
|
end
|
97
92
|
|
98
93
|
def forward(x)
|
99
|
-
|
100
|
-
|
101
|
-
|
94
|
+
x + @l1.(@param) + @l2.(@param)
|
95
|
+
end
|
96
|
+
|
97
|
+
def l1_lambda
|
98
|
+
@l1.l1_lambda
|
99
|
+
end
|
100
|
+
|
101
|
+
def l1_lambda=(lam)
|
102
|
+
@l1.l1_lambda = lam
|
103
|
+
end
|
104
|
+
|
105
|
+
def l2_lambda
|
106
|
+
@l2.l2_lambda
|
102
107
|
end
|
103
108
|
|
104
|
-
def
|
105
|
-
|
106
|
-
dparam[@param.data < 0] = -1
|
107
|
-
@param.grad += @l1_lambda * dparam
|
108
|
-
@param.grad += @l2_lambda * @param.data
|
109
|
+
def l2_lambda=(lam)
|
110
|
+
@l2.l2_lambda = lam
|
109
111
|
end
|
110
112
|
|
111
113
|
def to_hash
|
data/lib/dnn/core/tensor.rb
CHANGED
@@ -3,9 +3,49 @@ module DNN
|
|
3
3
|
attr_reader :data
|
4
4
|
attr_accessor :link
|
5
5
|
|
6
|
+
def self.convert(inputs)
|
7
|
+
if inputs.is_a?(Array)
|
8
|
+
inputs.map { |input| Tensor.new(input) }
|
9
|
+
else
|
10
|
+
Tensor.new(inputs)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
6
14
|
def initialize(data, link = nil)
|
7
15
|
@data = data
|
8
16
|
@link = link
|
9
17
|
end
|
18
|
+
|
19
|
+
def shape
|
20
|
+
@data.shape
|
21
|
+
end
|
22
|
+
|
23
|
+
def +@
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def -@
|
28
|
+
self * -1
|
29
|
+
end
|
30
|
+
|
31
|
+
def +(other)
|
32
|
+
Layers::Add.(self, other)
|
33
|
+
end
|
34
|
+
|
35
|
+
def -(other)
|
36
|
+
Layers::Sub.(self, other)
|
37
|
+
end
|
38
|
+
|
39
|
+
def *(other)
|
40
|
+
Layers::Mul.(self, other)
|
41
|
+
end
|
42
|
+
|
43
|
+
def /(other)
|
44
|
+
Layers::Div.(self, other)
|
45
|
+
end
|
46
|
+
|
47
|
+
def **(index)
|
48
|
+
Layers::Pow.new(index).(self)
|
49
|
+
end
|
10
50
|
end
|
11
51
|
end
|