ruby-dnn 0.10.1 → 0.10.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.
@@ -1,34 +1,34 @@
1
- # This class manages input datas and output datas together.
2
- class DNN::Dataset
3
- # @param [Numo::SFloat] x_datas input datas.
4
- # @param [Numo::SFloat] y_datas output datas.
5
- # @param [Bool] random Set true to return batches randomly. Setting false returns batches in order of index.
6
- def initialize(x_datas, y_datas, random = true)
7
- @x_datas = x_datas
8
- @y_datas = y_datas
9
- @random = random
10
- @num_datas = x_datas.shape[0]
11
- reset_indexs
12
- end
13
-
14
- # Return the next batch.
15
- # If the number of remaining data < batch size, if random = true, shuffle the data again and return a batch.
16
- # If random = false, all remaining data will be returned regardless of the batch size.
17
- def next_batch(batch_size)
18
- if @indexes.length < batch_size
19
- batch_indexes = @indexes unless @random
20
- reset_indexs
21
- batch_indexes = @indexes.shift(batch_size) if @random
22
- else
23
- batch_indexes = @indexes.shift(batch_size)
24
- end
25
- x_batch = @x_datas[batch_indexes, false]
26
- y_batch = @y_datas[batch_indexes, false]
27
- [x_batch, y_batch]
28
- end
29
-
30
- private def reset_indexs
31
- @indexes = @num_datas.times.to_a
32
- @indexes.shuffle! if @random
33
- end
34
- end
1
+ # This class manages input datas and output datas together.
2
+ class DNN::Dataset
3
+ # @param [Numo::SFloat] x_datas input datas.
4
+ # @param [Numo::SFloat] y_datas output datas.
5
+ # @param [Bool] random Set true to return batches randomly. Setting false returns batches in order of index.
6
+ def initialize(x_datas, y_datas, random = true)
7
+ @x_datas = x_datas
8
+ @y_datas = y_datas
9
+ @random = random
10
+ @num_datas = x_datas.shape[0]
11
+ reset_indexs
12
+ end
13
+
14
+ # Return the next batch.
15
+ # If the number of remaining data < batch size, if random = true, shuffle the data again and return a batch.
16
+ # If random = false, all remaining data will be returned regardless of the batch size.
17
+ def next_batch(batch_size)
18
+ if @indexes.length < batch_size
19
+ batch_indexes = @indexes unless @random
20
+ reset_indexs
21
+ batch_indexes = @indexes.shift(batch_size) if @random
22
+ else
23
+ batch_indexes = @indexes.shift(batch_size)
24
+ end
25
+ x_batch = @x_datas[batch_indexes, false]
26
+ y_batch = @y_datas[batch_indexes, false]
27
+ [x_batch, y_batch]
28
+ end
29
+
30
+ private def reset_indexs
31
+ @indexes = @num_datas.times.to_a
32
+ @indexes.shuffle! if @random
33
+ end
34
+ end
@@ -0,0 +1,56 @@
1
+ module DNN
2
+ module Layers
3
+
4
+ class Embedding < HasParamLayer
5
+ # @return [Integer] Return the input length.
6
+ attr_reader :input_length
7
+ # @return [Initializers::Initializer] Return the weight initializer.
8
+ attr_reader :weight_initializer
9
+
10
+ def self.from_hash(hash)
11
+ self.new(hash[:input_shape], hash[:input_length],
12
+ weight_initializer: DNN::Utils.from_hash(hash[:weight_initializer]))
13
+ end
14
+
15
+ # @param [Integer | Array] input_dim_or_shape Set input data dimension or shape.
16
+ # @param [Integer] input_length input Set the time series length of input data.
17
+ def initialize(input_dim_or_shape, input_length, weight_initializer: Initializers::RandomUniform.new)
18
+ super()
19
+ @input_shape = input_dim_or_shape.is_a?(Array) ? input_dim_or_shape : [input_dim_or_shape]
20
+ @input_length = input_length
21
+ @weight_initializer = weight_initializer
22
+ end
23
+
24
+ def build
25
+ @built = true
26
+ @params[:weight] = @weight = Param.new(Xumo::SFloat.new(@input_length), 0)
27
+ @weight_initializer.init_param(self, @weight)
28
+ @input_shape
29
+ end
30
+
31
+ def forward(x)
32
+ @x = x
33
+ y = Xumo::SFloat.zeros(*x.shape)
34
+ x.shape[0].times do |i|
35
+ y[i, false] = @weight.data[x[i, false]]
36
+ end
37
+ y
38
+ end
39
+
40
+ def backward(dy)
41
+ @weight.grad += Xumo::SFloat.zeros(*@weight.data.shape)
42
+ @x.shape[0].times do |i|
43
+ @x.shape[1].times do |j|
44
+ @weight.grad[@x[i, j]] += dy[i, j]
45
+ end
46
+ end
47
+ nil
48
+ end
49
+
50
+ def to_hash
51
+ super(input_shape: @input_shape, input_length: @input_length, weight_initializer: @weight_initializer.to_hash)
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -1,5 +1,5 @@
1
- module DNN
2
- class DNN_Error < StandardError; end
3
-
4
- class DNN_ShapeError < DNN_Error; end
5
- end
1
+ module DNN
2
+ class DNN_Error < StandardError; end
3
+
4
+ class DNN_ShapeError < DNN_Error; end
5
+ end
@@ -1,126 +1,126 @@
1
- module DNN
2
- module Initializers
3
-
4
- class Initializer
5
- def initialize(seed = false)
6
- @seed = seed == true ? rand(1 << 31) : seed
7
- end
8
-
9
- def init_param(layer, param)
10
- raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'init_params'")
11
- end
12
-
13
- def to_hash(merge_hash = nil)
14
- hash = {class: self.class.name, seed: @seed}
15
- hash.merge!(merge_hash) if merge_hash
16
- hash
17
- end
18
- end
19
-
20
-
21
- class Zeros < Initializer
22
- def init_param(layer, param)
23
- param.data = param.data.fill(0)
24
- end
25
- end
26
-
27
-
28
- class Const < Initializer
29
- attr_reader :const
30
-
31
- def self.from_hash(hash)
32
- self.new(hash[:const])
33
- end
34
-
35
- def initialize(const)
36
- super()
37
- @const = const
38
- end
39
-
40
- def init_param(layer, param)
41
- param.data = param.data.fill(@const)
42
- end
43
-
44
- def to_hash
45
- super({const: @const})
46
- end
47
- end
48
-
49
-
50
- class RandomNormal < Initializer
51
- attr_reader :mean
52
- attr_reader :std
53
-
54
- def self.from_hash(hash)
55
- self.new(hash[:mean], hash[:std], hash[:seed])
56
- end
57
-
58
- def initialize(mean = 0, std = 0.05, seed = true)
59
- super(seed)
60
- @mean = mean
61
- @std = std
62
- end
63
-
64
- def init_param(layer, param)
65
- Xumo::SFloat.srand(@seed)
66
- param.data = param.data.rand_norm(@mean, @std)
67
- end
68
-
69
- def to_hash
70
- super({mean: @mean, std: @std})
71
- end
72
- end
73
-
74
-
75
- class RandomUniform < Initializer
76
- attr_reader :min
77
- attr_reader :max
78
-
79
- def self.from_hash(hash)
80
- self.new(hash[:min], hash[:max], hash[:seed])
81
- end
82
-
83
- def initialize(min = -0.05, max = 0.05, seed = true)
84
- super(seed)
85
- @min = min
86
- @max = max
87
- end
88
-
89
- def init_param(layer, param)
90
- Xumo::SFloat.srand(@seed)
91
- param.data = param.data.rand(@min, @max)
92
- end
93
-
94
- def to_hash
95
- super({min: @min, max: @max})
96
- end
97
- end
98
-
99
-
100
- class Xavier < Initializer
101
- def initialize(seed = true)
102
- super
103
- end
104
-
105
- def init_param(layer, param)
106
- Xumo::SFloat.srand(@seed)
107
- num_prev_nodes = layer.input_shape.reduce(:*)
108
- param.data = param.data.rand_norm / Math.sqrt(num_prev_nodes)
109
- end
110
- end
111
-
112
-
113
- class He < Initializer
114
- def initialize(seed = true)
115
- super
116
- end
117
-
118
- def init_param(layer, param)
119
- Xumo::SFloat.srand(@seed)
120
- num_prev_nodes = layer.input_shape.reduce(:*)
121
- param.data = param.data.rand_norm / Math.sqrt(num_prev_nodes) * Math.sqrt(2)
122
- end
123
- end
124
-
125
- end
126
- end
1
+ module DNN
2
+ module Initializers
3
+
4
+ class Initializer
5
+ def initialize(seed = false)
6
+ @seed = seed == true ? rand(1 << 31) : seed
7
+ end
8
+
9
+ def init_param(layer, param)
10
+ raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'init_params'")
11
+ end
12
+
13
+ def to_hash(merge_hash = nil)
14
+ hash = {class: self.class.name, seed: @seed}
15
+ hash.merge!(merge_hash) if merge_hash
16
+ hash
17
+ end
18
+ end
19
+
20
+
21
+ class Zeros < Initializer
22
+ def init_param(layer, param)
23
+ param.data = param.data.fill(0)
24
+ end
25
+ end
26
+
27
+
28
+ class Const < Initializer
29
+ attr_reader :const
30
+
31
+ def self.from_hash(hash)
32
+ self.new(hash[:const])
33
+ end
34
+
35
+ def initialize(const)
36
+ super()
37
+ @const = const
38
+ end
39
+
40
+ def init_param(layer, param)
41
+ param.data = param.data.fill(@const)
42
+ end
43
+
44
+ def to_hash
45
+ super({const: @const})
46
+ end
47
+ end
48
+
49
+
50
+ class RandomNormal < Initializer
51
+ attr_reader :mean
52
+ attr_reader :std
53
+
54
+ def self.from_hash(hash)
55
+ self.new(hash[:mean], hash[:std], hash[:seed])
56
+ end
57
+
58
+ def initialize(mean = 0, std = 0.05, seed = true)
59
+ super(seed)
60
+ @mean = mean
61
+ @std = std
62
+ end
63
+
64
+ def init_param(layer, param)
65
+ Xumo::SFloat.srand(@seed)
66
+ param.data = param.data.rand_norm(@mean, @std)
67
+ end
68
+
69
+ def to_hash
70
+ super({mean: @mean, std: @std})
71
+ end
72
+ end
73
+
74
+
75
+ class RandomUniform < Initializer
76
+ attr_reader :min
77
+ attr_reader :max
78
+
79
+ def self.from_hash(hash)
80
+ self.new(hash[:min], hash[:max], hash[:seed])
81
+ end
82
+
83
+ def initialize(min = -0.05, max = 0.05, seed = true)
84
+ super(seed)
85
+ @min = min
86
+ @max = max
87
+ end
88
+
89
+ def init_param(layer, param)
90
+ Xumo::SFloat.srand(@seed)
91
+ param.data = param.data.rand(@min, @max)
92
+ end
93
+
94
+ def to_hash
95
+ super({min: @min, max: @max})
96
+ end
97
+ end
98
+
99
+
100
+ class Xavier < Initializer
101
+ def initialize(seed = true)
102
+ super
103
+ end
104
+
105
+ def init_param(layer, param)
106
+ Xumo::SFloat.srand(@seed)
107
+ num_prev_nodes = layer.input_shape.reduce(:*)
108
+ param.data = param.data.rand_norm / Math.sqrt(num_prev_nodes)
109
+ end
110
+ end
111
+
112
+
113
+ class He < Initializer
114
+ def initialize(seed = true)
115
+ super
116
+ end
117
+
118
+ def init_param(layer, param)
119
+ Xumo::SFloat.srand(@seed)
120
+ num_prev_nodes = layer.input_shape.reduce(:*)
121
+ param.data = param.data.rand_norm / Math.sqrt(num_prev_nodes) * Math.sqrt(2)
122
+ end
123
+ end
124
+
125
+ end
126
+ end
@@ -1,307 +1,307 @@
1
- module DNN
2
- module Layers
3
-
4
- # Super class of all optimizer classes.
5
- class Layer
6
- # @return [Bool] learning_phase Return the true if learning.
7
- attr_accessor :learning_phase
8
- # @return [Array] Return the shape of the input data.
9
- attr_reader :input_shape
10
-
11
- def initialize
12
- @built = false
13
- end
14
-
15
- # Build the layer.
16
- # @param [Array] input_shape Setting the shape of the input data.
17
- def build(input_shape)
18
- @input_shape = input_shape
19
- @learning_phase = true
20
- @built = true
21
- end
22
-
23
- # Does the layer have already been built?
24
- # @return [Bool] If layer have already been built then return true.
25
- def built?
26
- @built
27
- end
28
-
29
- # Forward propagation.
30
- def forward(x)
31
- raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'forward'")
32
- end
33
-
34
- # Backward propagation.
35
- def backward(dy)
36
- raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'backward'")
37
- end
38
-
39
- # Please reimplement this method as needed.
40
- # The default implementation return input_shape.
41
- # @return [Array] Return the shape of the output data.
42
- def output_shape
43
- @input_shape
44
- end
45
-
46
- # Layer to a hash.
47
- def to_hash(merge_hash = nil)
48
- hash = {class: self.class.name}
49
- hash.merge!(merge_hash) if merge_hash
50
- hash
51
- end
52
- end
53
-
54
-
55
- # This class is a superclass of all classes with learning parameters.
56
- class HasParamLayer < Layer
57
- # @return [Bool] trainable Setting false prevents learning of parameters.
58
- attr_accessor :trainable
59
- # @return [Array] The parameters of the layer.
60
- attr_reader :params
61
-
62
- def initialize
63
- super()
64
- @params = {}
65
- @trainable = true
66
- end
67
- end
68
-
69
-
70
- class InputLayer < Layer
71
- def self.from_hash(hash)
72
- self.new(hash[:input_shape])
73
- end
74
-
75
- def initialize(input_dim_or_shape)
76
- super()
77
- @input_shape = input_dim_or_shape.is_a?(Array) ? input_dim_or_shape : [input_dim_or_shape]
78
- end
79
-
80
- def build
81
- @built = true
82
- @input_shape
83
- end
84
-
85
- def forward(x)
86
- x
87
- end
88
-
89
- def backward(dy)
90
- dy
91
- end
92
-
93
- def to_hash
94
- super({input_shape: @input_shape})
95
- end
96
- end
97
-
98
-
99
- # It is a superclass of all connection layers.
100
- class Connection < HasParamLayer
101
- # @return [DNN::Initializers::Initializer] Weight initializer.
102
- attr_reader :weight_initializer
103
- # @return [DNN::Initializers::Initializer] Bias initializer.
104
- attr_reader :bias_initializer
105
- # @return [DNN::Regularizers::Regularizer] Weight regularization.
106
- attr_reader :weight_regularizer
107
- # @return [DNN::Regularizers::Regularizer] Bias regularization.
108
- attr_reader :bias_regularizer
109
-
110
- # @param [DNN::Initializers::Initializer] weight_initializer Weight initializer.
111
- # @param [DNN::Initializers::Initializer] bias_initializer Bias initializer.
112
- # @param [DNN::Regularizers::Regularizer] weight_regularizer Weight regularization.
113
- # @param [DNN::Regularizers::Regularizer] bias_regularizer Bias regularization.
114
- # @param [Bool] use_bias whether to use bias.
115
- def initialize(weight_initializer: Initializers::RandomNormal.new,
116
- bias_initializer: Initializers::Zeros.new,
117
- weight_regularizer: nil,
118
- bias_regularizer: nil,
119
- use_bias: true)
120
- super()
121
- @weight_initializer = weight_initializer
122
- @bias_initializer = bias_initializer
123
- @weight_regularizer = weight_regularizer
124
- @bias_regularizer = bias_regularizer
125
- @params[:weight] = @weight = Param.new(nil, 0)
126
- if use_bias
127
- @params[:bias] = @bias = Param.new(nil, 0)
128
- else
129
- @bias = nil
130
- end
131
- end
132
-
133
- def regularizers
134
- regularizers = []
135
- regularizers << @weight_regularizer if @weight_regularizer
136
- regularizers << @bias_regularizer if @bias_regularizer
137
- regularizers
138
- end
139
-
140
- # @return [Bool] Return whether to use bias.
141
- def use_bias
142
- @bias ? true : false
143
- end
144
-
145
- def to_hash(merge_hash)
146
- super({weight_initializer: @weight_initializer.to_hash,
147
- bias_initializer: @bias_initializer.to_hash,
148
- weight_regularizer: @weight_regularizer&.to_hash,
149
- bias_regularizer: @bias_regularizer&.to_hash,
150
- use_bias: use_bias}.merge(merge_hash))
151
- end
152
-
153
- private def init_weight_and_bias
154
- @weight_initializer.init_param(self, @weight)
155
- @weight_regularizer.param = @weight if @weight_regularizer
156
- if @bias
157
- @bias_initializer.init_param(self, @bias)
158
- @bias_regularizer.param = @bias if @bias_regularizer
159
- end
160
- end
161
- end
162
-
163
-
164
- # Full connnection layer.
165
- class Dense < Connection
166
- # @return [Integer] number of nodes.
167
- attr_reader :num_nodes
168
-
169
- def self.from_hash(hash)
170
- self.new(hash[:num_nodes],
171
- weight_initializer: Utils.from_hash(hash[:weight_initializer]),
172
- bias_initializer: Utils.from_hash(hash[:bias_initializer]),
173
- weight_regularizer: Utils.from_hash(hash[:weight_regularizer]),
174
- bias_regularizer: Utils.from_hash(hash[:bias_regularizer]),
175
- use_bias: hash[:use_bias])
176
- end
177
-
178
- # @param [Integer] num_nodes number of nodes.
179
- def initialize(num_nodes,
180
- weight_initializer: Initializers::RandomNormal.new,
181
- bias_initializer: Initializers::Zeros.new,
182
- weight_regularizer: nil,
183
- bias_regularizer: nil,
184
- use_bias: true)
185
- super(weight_initializer: weight_initializer, bias_initializer: bias_initializer,
186
- weight_regularizer: weight_regularizer, bias_regularizer: bias_regularizer, use_bias: use_bias)
187
- @num_nodes = num_nodes
188
- end
189
-
190
- def build(input_shape)
191
- super
192
- num_prev_nodes = input_shape[0]
193
- @weight.data = Xumo::SFloat.new(num_prev_nodes, @num_nodes)
194
- @bias.data = Xumo::SFloat.new(@num_nodes) if @bias
195
- init_weight_and_bias
196
- end
197
-
198
- def forward(x)
199
- @x = x
200
- y = x.dot(@weight.data)
201
- y += @bias.data if @bias
202
- y
203
- end
204
-
205
- def backward(dy)
206
- if @trainable
207
- @weight.grad += @x.transpose.dot(dy)
208
- @bias.grad += dy.sum(0) if @bias
209
- end
210
- dy.dot(@weight.data.transpose)
211
- end
212
-
213
- def output_shape
214
- [@num_nodes]
215
- end
216
-
217
- def to_hash
218
- super({num_nodes: @num_nodes})
219
- end
220
- end
221
-
222
-
223
- class Flatten < Layer
224
- def forward(x)
225
- x.reshape(x.shape[0], *output_shape)
226
- end
227
-
228
- def backward(dy)
229
- dy.reshape(dy.shape[0], *@input_shape)
230
- end
231
-
232
- def output_shape
233
- [@input_shape.reduce(:*)]
234
- end
235
- end
236
-
237
-
238
- class Reshape < Layer
239
- def self.from_hash(hash)
240
- self.new(hash[:output_shape])
241
- end
242
-
243
- def initialize(output_shape)
244
- super()
245
- @output_shape = output_shape
246
- end
247
-
248
- def forward(x)
249
- x.reshape(x.shape[0], *@output_shape)
250
- end
251
-
252
- def backward(dy)
253
- dy.reshape(dy.shape[0], *@input_shape)
254
- end
255
-
256
- def output_shape
257
- @output_shape
258
- end
259
-
260
- def to_hash
261
- super({output_shape: @output_shape})
262
- end
263
- end
264
-
265
-
266
- class Dropout < Layer
267
- # @return [Float] dropout ratio.
268
- attr_accessor :dropout_ratio
269
- # @return [Float] Use 'weight scaling inference rule'.
270
- attr_reader :use_scale
271
-
272
- def self.from_hash(hash)
273
- self.new(hash[:dropout_ratio], seed: hash[:seed], use_scale: hash[:use_scale])
274
- end
275
-
276
- def initialize(dropout_ratio = 0.5, seed: rand(1 << 31), use_scale: true)
277
- super()
278
- @dropout_ratio = dropout_ratio
279
- @seed = seed
280
- @use_scale = use_scale
281
- @mask = nil
282
- end
283
-
284
- def forward(x)
285
- if learning_phase
286
- Xumo::SFloat.srand(@seed)
287
- @mask = Xumo::SFloat.ones(*x.shape).rand < @dropout_ratio
288
- x[@mask] = 0
289
- else
290
- x *= (1 - @dropout_ratio) if @use_scale
291
- end
292
- x
293
- end
294
-
295
- def backward(dy)
296
- dy[@mask] = 0
297
- dy
298
- end
299
-
300
- def to_hash
301
- super({dropout_ratio: @dropout_ratio, seed: @seed, use_scale: @use_scale})
302
- end
303
- end
304
-
305
- end
306
-
307
- end
1
+ module DNN
2
+ module Layers
3
+
4
+ # Super class of all optimizer classes.
5
+ class Layer
6
+ # @return [Bool] learning_phase Return the true if learning.
7
+ attr_accessor :learning_phase
8
+ # @return [Array] Return the shape of the input data.
9
+ attr_reader :input_shape
10
+
11
+ def initialize
12
+ @built = false
13
+ end
14
+
15
+ # Build the layer.
16
+ # @param [Array] input_shape Setting the shape of the input data.
17
+ def build(input_shape)
18
+ @input_shape = input_shape
19
+ @learning_phase = true
20
+ @built = true
21
+ end
22
+
23
+ # Does the layer have already been built?
24
+ # @return [Bool] If layer have already been built then return true.
25
+ def built?
26
+ @built
27
+ end
28
+
29
+ # Forward propagation.
30
+ def forward(x)
31
+ raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'forward'")
32
+ end
33
+
34
+ # Backward propagation.
35
+ def backward(dy)
36
+ raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'backward'")
37
+ end
38
+
39
+ # Please reimplement this method as needed.
40
+ # The default implementation return input_shape.
41
+ # @return [Array] Return the shape of the output data.
42
+ def output_shape
43
+ @input_shape
44
+ end
45
+
46
+ # Layer to a hash.
47
+ def to_hash(merge_hash = nil)
48
+ hash = {class: self.class.name}
49
+ hash.merge!(merge_hash) if merge_hash
50
+ hash
51
+ end
52
+ end
53
+
54
+
55
+ # This class is a superclass of all classes with learning parameters.
56
+ class HasParamLayer < Layer
57
+ # @return [Bool] trainable Setting false prevents learning of parameters.
58
+ attr_accessor :trainable
59
+ # @return [Array] The parameters of the layer.
60
+ attr_reader :params
61
+
62
+ def initialize
63
+ super()
64
+ @params = {}
65
+ @trainable = true
66
+ end
67
+ end
68
+
69
+
70
+ class InputLayer < Layer
71
+ def self.from_hash(hash)
72
+ self.new(hash[:input_shape])
73
+ end
74
+
75
+ def initialize(input_dim_or_shape)
76
+ super()
77
+ @input_shape = input_dim_or_shape.is_a?(Array) ? input_dim_or_shape : [input_dim_or_shape]
78
+ end
79
+
80
+ def build
81
+ @built = true
82
+ @input_shape
83
+ end
84
+
85
+ def forward(x)
86
+ x
87
+ end
88
+
89
+ def backward(dy)
90
+ dy
91
+ end
92
+
93
+ def to_hash
94
+ super({input_shape: @input_shape})
95
+ end
96
+ end
97
+
98
+
99
+ # It is a superclass of all connection layers.
100
+ class Connection < HasParamLayer
101
+ # @return [DNN::Initializers::Initializer] Weight initializer.
102
+ attr_reader :weight_initializer
103
+ # @return [DNN::Initializers::Initializer] Bias initializer.
104
+ attr_reader :bias_initializer
105
+ # @return [DNN::Regularizers::Regularizer] Weight regularization.
106
+ attr_reader :weight_regularizer
107
+ # @return [DNN::Regularizers::Regularizer] Bias regularization.
108
+ attr_reader :bias_regularizer
109
+
110
+ # @param [DNN::Initializers::Initializer] weight_initializer Weight initializer.
111
+ # @param [DNN::Initializers::Initializer] bias_initializer Bias initializer.
112
+ # @param [DNN::Regularizers::Regularizer] weight_regularizer Weight regularization.
113
+ # @param [DNN::Regularizers::Regularizer] bias_regularizer Bias regularization.
114
+ # @param [Bool] use_bias whether to use bias.
115
+ def initialize(weight_initializer: Initializers::RandomNormal.new,
116
+ bias_initializer: Initializers::Zeros.new,
117
+ weight_regularizer: nil,
118
+ bias_regularizer: nil,
119
+ use_bias: true)
120
+ super()
121
+ @weight_initializer = weight_initializer
122
+ @bias_initializer = bias_initializer
123
+ @weight_regularizer = weight_regularizer
124
+ @bias_regularizer = bias_regularizer
125
+ @params[:weight] = @weight = Param.new(nil, 0)
126
+ if use_bias
127
+ @params[:bias] = @bias = Param.new(nil, 0)
128
+ else
129
+ @bias = nil
130
+ end
131
+ end
132
+
133
+ def regularizers
134
+ regularizers = []
135
+ regularizers << @weight_regularizer if @weight_regularizer
136
+ regularizers << @bias_regularizer if @bias_regularizer
137
+ regularizers
138
+ end
139
+
140
+ # @return [Bool] Return whether to use bias.
141
+ def use_bias
142
+ @bias ? true : false
143
+ end
144
+
145
+ def to_hash(merge_hash)
146
+ super({weight_initializer: @weight_initializer.to_hash,
147
+ bias_initializer: @bias_initializer.to_hash,
148
+ weight_regularizer: @weight_regularizer&.to_hash,
149
+ bias_regularizer: @bias_regularizer&.to_hash,
150
+ use_bias: use_bias}.merge(merge_hash))
151
+ end
152
+
153
+ private def init_weight_and_bias
154
+ @weight_initializer.init_param(self, @weight)
155
+ @weight_regularizer.param = @weight if @weight_regularizer
156
+ if @bias
157
+ @bias_initializer.init_param(self, @bias)
158
+ @bias_regularizer.param = @bias if @bias_regularizer
159
+ end
160
+ end
161
+ end
162
+
163
+
164
+ # Full connnection layer.
165
+ class Dense < Connection
166
+ # @return [Integer] number of nodes.
167
+ attr_reader :num_nodes
168
+
169
+ def self.from_hash(hash)
170
+ self.new(hash[:num_nodes],
171
+ weight_initializer: Utils.from_hash(hash[:weight_initializer]),
172
+ bias_initializer: Utils.from_hash(hash[:bias_initializer]),
173
+ weight_regularizer: Utils.from_hash(hash[:weight_regularizer]),
174
+ bias_regularizer: Utils.from_hash(hash[:bias_regularizer]),
175
+ use_bias: hash[:use_bias])
176
+ end
177
+
178
+ # @param [Integer] num_nodes number of nodes.
179
+ def initialize(num_nodes,
180
+ weight_initializer: Initializers::RandomNormal.new,
181
+ bias_initializer: Initializers::Zeros.new,
182
+ weight_regularizer: nil,
183
+ bias_regularizer: nil,
184
+ use_bias: true)
185
+ super(weight_initializer: weight_initializer, bias_initializer: bias_initializer,
186
+ weight_regularizer: weight_regularizer, bias_regularizer: bias_regularizer, use_bias: use_bias)
187
+ @num_nodes = num_nodes
188
+ end
189
+
190
+ def build(input_shape)
191
+ super
192
+ num_prev_nodes = input_shape[0]
193
+ @weight.data = Xumo::SFloat.new(num_prev_nodes, @num_nodes)
194
+ @bias.data = Xumo::SFloat.new(@num_nodes) if @bias
195
+ init_weight_and_bias
196
+ end
197
+
198
+ def forward(x)
199
+ @x = x
200
+ y = x.dot(@weight.data)
201
+ y += @bias.data if @bias
202
+ y
203
+ end
204
+
205
+ def backward(dy)
206
+ if @trainable
207
+ @weight.grad += @x.transpose.dot(dy)
208
+ @bias.grad += dy.sum(0) if @bias
209
+ end
210
+ dy.dot(@weight.data.transpose)
211
+ end
212
+
213
+ def output_shape
214
+ [@num_nodes]
215
+ end
216
+
217
+ def to_hash
218
+ super({num_nodes: @num_nodes})
219
+ end
220
+ end
221
+
222
+
223
+ class Flatten < Layer
224
+ def forward(x)
225
+ x.reshape(x.shape[0], *output_shape)
226
+ end
227
+
228
+ def backward(dy)
229
+ dy.reshape(dy.shape[0], *@input_shape)
230
+ end
231
+
232
+ def output_shape
233
+ [@input_shape.reduce(:*)]
234
+ end
235
+ end
236
+
237
+
238
+ class Reshape < Layer
239
+ def self.from_hash(hash)
240
+ self.new(hash[:output_shape])
241
+ end
242
+
243
+ def initialize(output_shape)
244
+ super()
245
+ @output_shape = output_shape
246
+ end
247
+
248
+ def forward(x)
249
+ x.reshape(x.shape[0], *@output_shape)
250
+ end
251
+
252
+ def backward(dy)
253
+ dy.reshape(dy.shape[0], *@input_shape)
254
+ end
255
+
256
+ def output_shape
257
+ @output_shape
258
+ end
259
+
260
+ def to_hash
261
+ super({output_shape: @output_shape})
262
+ end
263
+ end
264
+
265
+
266
+ class Dropout < Layer
267
+ # @return [Float] dropout ratio.
268
+ attr_accessor :dropout_ratio
269
+ # @return [Float] Use 'weight scaling inference rule'.
270
+ attr_reader :use_scale
271
+
272
+ def self.from_hash(hash)
273
+ self.new(hash[:dropout_ratio], seed: hash[:seed], use_scale: hash[:use_scale])
274
+ end
275
+
276
+ def initialize(dropout_ratio = 0.5, seed: rand(1 << 31), use_scale: true)
277
+ super()
278
+ @dropout_ratio = dropout_ratio
279
+ @seed = seed
280
+ @use_scale = use_scale
281
+ @mask = nil
282
+ end
283
+
284
+ def forward(x)
285
+ if learning_phase
286
+ Xumo::SFloat.srand(@seed)
287
+ @mask = Xumo::SFloat.ones(*x.shape).rand < @dropout_ratio
288
+ x[@mask] = 0
289
+ else
290
+ x *= (1 - @dropout_ratio) if @use_scale
291
+ end
292
+ x
293
+ end
294
+
295
+ def backward(dy)
296
+ dy[@mask] = 0
297
+ dy
298
+ end
299
+
300
+ def to_hash
301
+ super({dropout_ratio: @dropout_ratio, seed: @seed, use_scale: @use_scale})
302
+ end
303
+ end
304
+
305
+ end
306
+
307
+ end