ruby-dnn 0.13.4 → 0.14.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/cifar100_example.rb +1 -2
- data/examples/cifar10_example.rb +1 -2
- data/examples/dcgan/dcgan.rb +19 -9
- data/examples/dcgan/imgen.rb +9 -2
- data/examples/dcgan/train.rb +7 -22
- data/examples/iris_example.rb +1 -2
- data/examples/mnist_conv2d_example.rb +1 -2
- data/examples/mnist_define_by_run.rb +1 -2
- data/examples/mnist_example.rb +1 -2
- data/examples/mnist_lstm_example.rb +1 -2
- data/examples/xor_example.rb +2 -3
- data/lib/dnn.rb +2 -0
- data/lib/dnn/core/activations.rb +11 -18
- data/lib/dnn/core/callbacks.rb +136 -0
- data/lib/dnn/core/cnn_layers.rb +26 -33
- data/lib/dnn/core/embedding.rb +20 -2
- data/lib/dnn/core/error.rb +0 -2
- data/lib/dnn/core/initializers.rb +2 -8
- data/lib/dnn/core/iterator.rb +17 -13
- data/lib/dnn/core/layers.rb +38 -34
- data/lib/dnn/core/link.rb +1 -2
- data/lib/dnn/core/losses.rb +21 -14
- data/lib/dnn/core/merge_layers.rb +7 -8
- data/lib/dnn/core/models.rb +134 -125
- data/lib/dnn/core/normalizations.rb +2 -2
- data/lib/dnn/core/optimizers.rb +20 -25
- data/lib/dnn/core/regularizers.rb +6 -7
- data/lib/dnn/core/rnn_layers.rb +15 -21
- data/lib/dnn/core/savers.rb +9 -7
- data/lib/dnn/core/tensor.rb +11 -0
- data/lib/dnn/core/utils.rb +1 -1
- data/lib/dnn/image.rb +22 -1
- data/lib/dnn/version.rb +1 -1
- metadata +4 -2
data/lib/dnn/core/cnn_layers.rb
CHANGED
@@ -83,7 +83,6 @@ module DNN
|
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
-
|
87
86
|
class Conv2D < Connection
|
88
87
|
include Conv2DUtils
|
89
88
|
|
@@ -114,7 +113,7 @@ module DNN
|
|
114
113
|
|
115
114
|
def build(input_shape)
|
116
115
|
unless input_shape.length == 3
|
117
|
-
raise DNN_ShapeError
|
116
|
+
raise DNN_ShapeError, "Input shape is #{input_shape}. But input shape must be 3 dimensional."
|
118
117
|
end
|
119
118
|
super
|
120
119
|
prev_h, prev_w, num_prev_filters = *input_shape
|
@@ -122,12 +121,12 @@ module DNN
|
|
122
121
|
@bias.data = Xumo::SFloat.new(@num_filters) if @bias
|
123
122
|
init_weight_and_bias
|
124
123
|
@pad_size = if @padding == true
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
124
|
+
calc_conv2d_padding_size(prev_h, prev_w, *@filter_size, @strides)
|
125
|
+
elsif @padding.is_a?(Array)
|
126
|
+
@padding
|
127
|
+
else
|
128
|
+
[0, 0]
|
129
|
+
end
|
131
130
|
@out_size = calc_conv2d_out_size(prev_h, prev_w, *@filter_size, *@pad_size, @strides)
|
132
131
|
end
|
133
132
|
|
@@ -186,7 +185,6 @@ module DNN
|
|
186
185
|
end
|
187
186
|
end
|
188
187
|
|
189
|
-
|
190
188
|
class Conv2DTranspose < Connection
|
191
189
|
include Conv2DUtils
|
192
190
|
|
@@ -217,7 +215,7 @@ module DNN
|
|
217
215
|
|
218
216
|
def build(input_shape)
|
219
217
|
unless input_shape.length == 3
|
220
|
-
raise DNN_ShapeError
|
218
|
+
raise DNN_ShapeError, "Input shape is #{input_shape}. But input shape must be 3 dimensional."
|
221
219
|
end
|
222
220
|
super
|
223
221
|
prev_h, prev_w, num_prev_filters = *input_shape
|
@@ -225,12 +223,12 @@ module DNN
|
|
225
223
|
@bias.data = Xumo::SFloat.new(@num_filters) if @bias
|
226
224
|
init_weight_and_bias
|
227
225
|
@pad_size = if @padding == true
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
226
|
+
calc_conv2d_transpose_padding_size(prev_h, prev_w, *@filter_size, @strides)
|
227
|
+
elsif @padding.is_a?(Array)
|
228
|
+
@padding
|
229
|
+
else
|
230
|
+
[0, 0]
|
231
|
+
end
|
234
232
|
@out_size = calc_conv2d_transpose_out_size(prev_h, prev_w, *@filter_size, *@pad_size, @strides)
|
235
233
|
end
|
236
234
|
|
@@ -291,7 +289,6 @@ module DNN
|
|
291
289
|
end
|
292
290
|
end
|
293
291
|
|
294
|
-
|
295
292
|
# Super class of all pooling2D class.
|
296
293
|
class Pool2D < Layer
|
297
294
|
include Conv2DUtils
|
@@ -308,27 +305,27 @@ module DNN
|
|
308
305
|
super()
|
309
306
|
@pool_size = pool_size.is_a?(Integer) ? [pool_size, pool_size] : pool_size
|
310
307
|
@strides = if strides
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
308
|
+
strides.is_a?(Integer) ? [strides, strides] : strides
|
309
|
+
else
|
310
|
+
@pool_size.clone
|
311
|
+
end
|
315
312
|
@padding = padding.is_a?(Integer) ? [padding, padding] : padding
|
316
313
|
end
|
317
314
|
|
318
315
|
def build(input_shape)
|
319
316
|
unless input_shape.length == 3
|
320
|
-
raise DNN_ShapeError
|
317
|
+
raise DNN_ShapeError, "Input shape is #{input_shape}. But input shape must be 3 dimensional."
|
321
318
|
end
|
322
319
|
super
|
323
320
|
prev_h, prev_w = input_shape[0..1]
|
324
321
|
@num_channel = input_shape[2]
|
325
322
|
@pad_size = if @padding == true
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
323
|
+
calc_conv2d_padding_size(prev_h, prev_w, *@pool_size, @strides)
|
324
|
+
elsif @padding.is_a?(Array)
|
325
|
+
@padding
|
326
|
+
else
|
327
|
+
[0, 0]
|
328
|
+
end
|
332
329
|
@out_size = calc_conv2d_out_size(prev_h, prev_w, *@pool_size, *@pad_size, @strides)
|
333
330
|
end
|
334
331
|
|
@@ -345,10 +342,8 @@ module DNN
|
|
345
342
|
def load_hash(hash)
|
346
343
|
initialize(hash[:pool_size], strides: hash[:strides], padding: hash[:padding])
|
347
344
|
end
|
348
|
-
|
349
345
|
end
|
350
346
|
|
351
|
-
|
352
347
|
class MaxPool2D < Pool2D
|
353
348
|
def forward(x)
|
354
349
|
x = zero_padding(x, @pad_size) if @padding
|
@@ -368,7 +363,6 @@ module DNN
|
|
368
363
|
end
|
369
364
|
end
|
370
365
|
|
371
|
-
|
372
366
|
class AvgPool2D < Pool2D
|
373
367
|
def forward(x)
|
374
368
|
x = zero_padding(x, @pad_size) if @padding
|
@@ -391,7 +385,6 @@ module DNN
|
|
391
385
|
end
|
392
386
|
end
|
393
387
|
|
394
|
-
|
395
388
|
class UnPool2D < Layer
|
396
389
|
include Conv2DUtils
|
397
390
|
|
@@ -405,7 +398,7 @@ module DNN
|
|
405
398
|
|
406
399
|
def build(input_shape)
|
407
400
|
unless input_shape.length == 3
|
408
|
-
raise DNN_ShapeError
|
401
|
+
raise DNN_ShapeError, "Input shape is #{input_shape}. But input shape must be 3 dimensional."
|
409
402
|
end
|
410
403
|
super
|
411
404
|
prev_h, prev_w = input_shape[0..1]
|
data/lib/dnn/core/embedding.rb
CHANGED
@@ -21,9 +21,9 @@ module DNN
|
|
21
21
|
@weight_regularizer = weight_regularizer
|
22
22
|
end
|
23
23
|
|
24
|
-
def call(
|
24
|
+
def call(input_tensor)
|
25
25
|
build unless built?
|
26
|
-
|
26
|
+
Tensor.new(forward(input_tensor.data), Link.new(nil, self))
|
27
27
|
end
|
28
28
|
|
29
29
|
def build
|
@@ -56,6 +56,24 @@ module DNN
|
|
56
56
|
@weight_regularizer ? [@weight_regularizer] : []
|
57
57
|
end
|
58
58
|
|
59
|
+
def to_proc
|
60
|
+
method(:call).to_proc
|
61
|
+
end
|
62
|
+
|
63
|
+
def >>(layer)
|
64
|
+
if RUBY_VERSION < "2.6.0"
|
65
|
+
raise DNN_Error, "Function composition is not supported before ruby version 2.6.0."
|
66
|
+
end
|
67
|
+
to_proc >> layer
|
68
|
+
end
|
69
|
+
|
70
|
+
def <<(layer)
|
71
|
+
if RUBY_VERSION < "2.6.0"
|
72
|
+
raise DNN_Error, "Function composition is not supported before ruby version 2.6.0."
|
73
|
+
end
|
74
|
+
to_proc << layer
|
75
|
+
end
|
76
|
+
|
59
77
|
def to_hash
|
60
78
|
super(input_shape: @input_shape, input_length: @input_length,
|
61
79
|
weight_initializer: @weight_initializer.to_hash, weight_regularizer: @weight_regularizer&.to_hash)
|
data/lib/dnn/core/error.rb
CHANGED
@@ -6,7 +6,7 @@ module DNN
|
|
6
6
|
return nil unless hash
|
7
7
|
initializer_class = DNN.const_get(hash[:class])
|
8
8
|
initializer = initializer_class.allocate
|
9
|
-
raise DNN_Error
|
9
|
+
raise DNN_Error, "#{initializer.class} is not an instance of #{self} class." unless initializer.is_a?(self)
|
10
10
|
initializer.load_hash(hash)
|
11
11
|
initializer
|
12
12
|
end
|
@@ -21,7 +21,7 @@ module DNN
|
|
21
21
|
# @param [DNN::Layers::Layer] layer Layer that owns learning parameters.
|
22
22
|
# @param [DNN::Param] param Learning parameter to be initialized.
|
23
23
|
def init_param(layer, param)
|
24
|
-
raise NotImplementedError
|
24
|
+
raise NotImplementedError, "Class '#{self.class.name}' has implement method 'init_param'"
|
25
25
|
end
|
26
26
|
|
27
27
|
def to_hash(merge_hash = nil)
|
@@ -35,14 +35,12 @@ module DNN
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
|
39
38
|
class Zeros < Initializer
|
40
39
|
def init_param(layer, param)
|
41
40
|
param.data = param.data.fill(0)
|
42
41
|
end
|
43
42
|
end
|
44
43
|
|
45
|
-
|
46
44
|
class Const < Initializer
|
47
45
|
attr_reader :const
|
48
46
|
|
@@ -65,7 +63,6 @@ module DNN
|
|
65
63
|
end
|
66
64
|
end
|
67
65
|
|
68
|
-
|
69
66
|
class RandomNormal < Initializer
|
70
67
|
attr_reader :mean
|
71
68
|
attr_reader :std
|
@@ -92,7 +89,6 @@ module DNN
|
|
92
89
|
end
|
93
90
|
end
|
94
91
|
|
95
|
-
|
96
92
|
class RandomUniform < Initializer
|
97
93
|
attr_reader :min
|
98
94
|
attr_reader :max
|
@@ -119,7 +115,6 @@ module DNN
|
|
119
115
|
end
|
120
116
|
end
|
121
117
|
|
122
|
-
|
123
118
|
class Xavier < Initializer
|
124
119
|
def initialize(seed: true)
|
125
120
|
super
|
@@ -132,7 +127,6 @@ module DNN
|
|
132
127
|
end
|
133
128
|
end
|
134
129
|
|
135
|
-
|
136
130
|
class He < Initializer
|
137
131
|
def initialize(seed: true)
|
138
132
|
super
|
data/lib/dnn/core/iterator.rb
CHANGED
@@ -1,13 +1,18 @@
|
|
1
1
|
module DNN
|
2
2
|
# This class manages input datas and output datas together.
|
3
3
|
class Iterator
|
4
|
+
attr_reader :num_datas
|
5
|
+
attr_reader :last_round_down
|
6
|
+
|
4
7
|
# @param [Numo::SFloat] x_datas input datas.
|
5
8
|
# @param [Numo::SFloat] y_datas output datas.
|
6
9
|
# @param [Boolean] random Set true to return batches randomly. Setting false returns batches in order of index.
|
7
|
-
|
10
|
+
# @param [Boolean] last_round_down Set true to round down for last batch data when call foreach.
|
11
|
+
def initialize(x_datas, y_datas, random: true, last_round_down: false)
|
8
12
|
@x_datas = x_datas
|
9
13
|
@y_datas = y_datas
|
10
14
|
@random = random
|
15
|
+
@last_round_down = last_round_down
|
11
16
|
@num_datas = x_datas.is_a?(Array) ? x_datas[0].shape[0] : x_datas.shape[0]
|
12
17
|
reset
|
13
18
|
end
|
@@ -15,7 +20,7 @@ module DNN
|
|
15
20
|
# Return the next batch.
|
16
21
|
# @param [Integer] batch_size Required batch size.
|
17
22
|
def next_batch(batch_size)
|
18
|
-
raise DNN_Error
|
23
|
+
raise DNN_Error, "This iterator has not next batch. Please call reset." unless has_next?
|
19
24
|
if @indexes.length <= batch_size
|
20
25
|
batch_indexes = @indexes
|
21
26
|
@has_next = false
|
@@ -23,15 +28,15 @@ module DNN
|
|
23
28
|
batch_indexes = @indexes.shift(batch_size)
|
24
29
|
end
|
25
30
|
x_batch = if @x_datas.is_a?(Array)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
31
|
+
@x_datas.map { |datas| datas[batch_indexes, false] }
|
32
|
+
else
|
33
|
+
@x_datas[batch_indexes, false]
|
34
|
+
end
|
30
35
|
y_batch = if @y_datas.is_a?(Array)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
36
|
+
@y_datas.map { |datas| datas[batch_indexes, false] }
|
37
|
+
else
|
38
|
+
@y_datas[batch_indexes, false]
|
39
|
+
end
|
35
40
|
[x_batch, y_batch]
|
36
41
|
end
|
37
42
|
|
@@ -48,11 +53,10 @@ module DNN
|
|
48
53
|
end
|
49
54
|
|
50
55
|
def foreach(batch_size, &block)
|
51
|
-
|
52
|
-
|
56
|
+
steps = @last_round_down ? @num_datas / batch_size : (@num_datas.to_f / batch_size).ceil
|
57
|
+
steps.times do |step|
|
53
58
|
x_batch, y_batch = next_batch(batch_size)
|
54
59
|
block.call(x_batch, y_batch, step)
|
55
|
-
step += 1
|
56
60
|
end
|
57
61
|
reset
|
58
62
|
end
|
data/lib/dnn/core/layers.rb
CHANGED
@@ -7,15 +7,16 @@ module DNN
|
|
7
7
|
attr_reader :input_shape
|
8
8
|
|
9
9
|
def self.call(x, *args)
|
10
|
-
|
10
|
+
new(*args).(x)
|
11
11
|
end
|
12
12
|
|
13
13
|
def self.from_hash(hash)
|
14
14
|
return nil unless hash
|
15
15
|
layer_class = DNN.const_get(hash[:class])
|
16
16
|
layer = layer_class.allocate
|
17
|
-
raise DNN_Error
|
17
|
+
raise DNN_Error, "#{layer.class} is not an instance of #{self} class." unless layer.is_a?(self)
|
18
18
|
layer.load_hash(hash)
|
19
|
+
layer.name = hash[:name]&.to_sym
|
19
20
|
layer
|
20
21
|
end
|
21
22
|
|
@@ -25,13 +26,15 @@ module DNN
|
|
25
26
|
end
|
26
27
|
|
27
28
|
# Forward propagation and create a link.
|
28
|
-
# @param [
|
29
|
-
|
30
|
-
|
29
|
+
# @param [Tensor] input_tensor Input tensor.
|
30
|
+
# @return [Tensor] Output tensor.
|
31
|
+
def call(input_tensor)
|
32
|
+
x = input_tensor.data
|
33
|
+
prev_link = input_tensor.link
|
31
34
|
build(x.shape[1..-1]) unless built?
|
32
35
|
y = forward(x)
|
33
36
|
link = Link.new(prev_link, self)
|
34
|
-
|
37
|
+
Tensor.new(y, link)
|
35
38
|
end
|
36
39
|
|
37
40
|
# Build the layer.
|
@@ -49,13 +52,13 @@ module DNN
|
|
49
52
|
# Forward propagation.
|
50
53
|
# @param [Numo::SFloat] x Input data.
|
51
54
|
def forward(x)
|
52
|
-
raise NotImplementedError
|
55
|
+
raise NotImplementedError, "Class '#{self.class.name}' has implement method 'forward'"
|
53
56
|
end
|
54
57
|
|
55
58
|
# Backward propagation.
|
56
59
|
# @param [Numo::SFloat] dy Differential value of output data.
|
57
60
|
def backward(dy)
|
58
|
-
raise NotImplementedError
|
61
|
+
raise NotImplementedError, "Class '#{self.class.name}' has implement method 'backward'"
|
59
62
|
end
|
60
63
|
|
61
64
|
# Please reimplement this method as needed.
|
@@ -77,7 +80,6 @@ module DNN
|
|
77
80
|
end
|
78
81
|
end
|
79
82
|
|
80
|
-
|
81
83
|
# This class is a superclass of all classes with learning parameters.
|
82
84
|
class HasParamLayer < Layer
|
83
85
|
# @return [Boolean] Setting false prevents learning of parameters.
|
@@ -90,15 +92,13 @@ module DNN
|
|
90
92
|
|
91
93
|
# @return [Array] The parameters of the layer.
|
92
94
|
def get_params
|
93
|
-
raise NotImplementedError
|
95
|
+
raise NotImplementedError, "Class '#{self.class.name}' has implement method 'get_params'"
|
94
96
|
end
|
95
97
|
end
|
96
98
|
|
97
|
-
|
98
99
|
class InputLayer < Layer
|
99
|
-
def self.call(
|
100
|
-
shape
|
101
|
-
self.new(shape[1..-1]).(input)
|
100
|
+
def self.call(input_tensor)
|
101
|
+
new(input_tensor.data.shape[1..-1]).(input_tensor)
|
102
102
|
end
|
103
103
|
|
104
104
|
# @param [Array] input_dim_or_shape Setting the shape or dimension of the input data.
|
@@ -107,16 +107,9 @@ module DNN
|
|
107
107
|
@input_shape = input_dim_or_shape.is_a?(Array) ? input_dim_or_shape : [input_dim_or_shape]
|
108
108
|
end
|
109
109
|
|
110
|
-
def call(
|
110
|
+
def call(input_tensor)
|
111
111
|
build unless built?
|
112
|
-
|
113
|
-
x, prev_link = *input
|
114
|
-
else
|
115
|
-
x = input
|
116
|
-
prev_link = nil
|
117
|
-
end
|
118
|
-
link = prev_link ? Link.new(prev_link, self) : Link.new(nil, self)
|
119
|
-
[forward(x), link]
|
112
|
+
Tensor.new(forward(input_tensor.data), Link.new(input_tensor&.link, self))
|
120
113
|
end
|
121
114
|
|
122
115
|
def build
|
@@ -125,7 +118,7 @@ module DNN
|
|
125
118
|
|
126
119
|
def forward(x)
|
127
120
|
unless x.shape[1..-1] == @input_shape
|
128
|
-
raise DNN_ShapeError
|
121
|
+
raise DNN_ShapeError, "The shape of x does not match the input shape. input shape is #{@input_shape}, but x shape is #{x.shape[1..-1]}."
|
129
122
|
end
|
130
123
|
x
|
131
124
|
end
|
@@ -134,6 +127,24 @@ module DNN
|
|
134
127
|
dy
|
135
128
|
end
|
136
129
|
|
130
|
+
def to_proc
|
131
|
+
method(:call).to_proc
|
132
|
+
end
|
133
|
+
|
134
|
+
def >>(layer)
|
135
|
+
if RUBY_VERSION < "2.6.0"
|
136
|
+
raise DNN_Error, "Function composition is not supported before ruby version 2.6.0."
|
137
|
+
end
|
138
|
+
to_proc >> layer
|
139
|
+
end
|
140
|
+
|
141
|
+
def <<(layer)
|
142
|
+
if RUBY_VERSION < "2.6.0"
|
143
|
+
raise DNN_Error, "Function composition is not supported before ruby version 2.6.0."
|
144
|
+
end
|
145
|
+
to_proc << layer
|
146
|
+
end
|
147
|
+
|
137
148
|
def to_hash
|
138
149
|
super(input_shape: @input_shape)
|
139
150
|
end
|
@@ -143,7 +154,6 @@ module DNN
|
|
143
154
|
end
|
144
155
|
end
|
145
156
|
|
146
|
-
|
147
157
|
# It is a superclass of all connection layers.
|
148
158
|
class Connection < HasParamLayer
|
149
159
|
attr_reader :weight
|
@@ -206,7 +216,6 @@ module DNN
|
|
206
216
|
end
|
207
217
|
end
|
208
218
|
|
209
|
-
|
210
219
|
class Dense < Connection
|
211
220
|
attr_reader :num_nodes
|
212
221
|
|
@@ -224,7 +233,7 @@ module DNN
|
|
224
233
|
|
225
234
|
def build(input_shape)
|
226
235
|
unless input_shape.length == 1
|
227
|
-
raise DNN_ShapeError
|
236
|
+
raise DNN_ShapeError, "Input shape is #{input_shape}. But input shape must be 1 dimensional."
|
228
237
|
end
|
229
238
|
super
|
230
239
|
num_prev_nodes = input_shape[0]
|
@@ -266,7 +275,6 @@ module DNN
|
|
266
275
|
end
|
267
276
|
end
|
268
277
|
|
269
|
-
|
270
278
|
class Flatten < Layer
|
271
279
|
def forward(x)
|
272
280
|
x.reshape(x.shape[0], *output_shape)
|
@@ -281,8 +289,9 @@ module DNN
|
|
281
289
|
end
|
282
290
|
end
|
283
291
|
|
284
|
-
|
285
292
|
class Reshape < Layer
|
293
|
+
attr_reader :output_shape
|
294
|
+
|
286
295
|
def initialize(output_shape)
|
287
296
|
super()
|
288
297
|
@output_shape = output_shape
|
@@ -296,10 +305,6 @@ module DNN
|
|
296
305
|
dy.reshape(dy.shape[0], *@input_shape)
|
297
306
|
end
|
298
307
|
|
299
|
-
def output_shape
|
300
|
-
@output_shape
|
301
|
-
end
|
302
|
-
|
303
308
|
def to_hash
|
304
309
|
super(output_shape: @output_shape)
|
305
310
|
end
|
@@ -309,7 +314,6 @@ module DNN
|
|
309
314
|
end
|
310
315
|
end
|
311
316
|
|
312
|
-
|
313
317
|
class Dropout < Layer
|
314
318
|
attr_accessor :dropout_ratio
|
315
319
|
attr_reader :use_scale
|