ruby-dnn 0.10.1 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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