ruby-dnn 0.9.4 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +39 -3
- data/Rakefile +6 -0
- data/examples/cifar100_example.rb +71 -0
- data/examples/cifar10_example.rb +2 -1
- data/examples/iris_example.rb +2 -1
- data/examples/mnist_conv2d_example.rb +2 -1
- data/examples/mnist_example.rb +2 -3
- data/examples/mnist_lstm_example.rb +2 -1
- data/ext/cifar_loader/cifar_loader.c +77 -0
- data/ext/cifar_loader/extconf.rb +3 -0
- data/lib/dnn.rb +1 -0
- data/lib/dnn/{lib/cifar10.rb → cifar10.rb} +9 -11
- data/lib/dnn/cifar100.rb +49 -0
- data/lib/dnn/core/activations.rb +28 -24
- data/lib/dnn/core/cnn_layers.rb +216 -94
- data/lib/dnn/core/dataset.rb +21 -5
- data/lib/dnn/core/initializers.rb +3 -3
- data/lib/dnn/core/layers.rb +81 -150
- data/lib/dnn/core/losses.rb +88 -49
- data/lib/dnn/core/model.rb +97 -74
- data/lib/dnn/core/normalizations.rb +72 -0
- data/lib/dnn/core/optimizers.rb +171 -78
- data/lib/dnn/core/regularizers.rb +92 -22
- data/lib/dnn/core/rnn_layers.rb +146 -121
- data/lib/dnn/core/utils.rb +4 -3
- data/lib/dnn/{lib/downloader.rb → downloader.rb} +5 -1
- data/lib/dnn/{lib/image.rb → image.rb} +1 -1
- data/lib/dnn/{lib/iris.rb → iris.rb} +1 -1
- data/lib/dnn/{lib/mnist.rb → mnist.rb} +4 -3
- data/lib/dnn/version.rb +1 -1
- data/ruby-dnn.gemspec +1 -1
- metadata +13 -12
- data/API-Reference.ja.md +0 -978
- data/LIB-API-Reference.ja.md +0 -97
- data/ext/cifar10_loader/cifar10_loader.c +0 -44
- data/ext/cifar10_loader/extconf.rb +0 -3
data/lib/dnn/core/dataset.rb
CHANGED
@@ -1,18 +1,34 @@
|
|
1
|
+
# This class manages input datas and output datas together.
|
1
2
|
class DNN::Dataset
|
2
|
-
|
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)
|
3
7
|
@x_datas = x_datas
|
4
8
|
@y_datas = y_datas
|
9
|
+
@random = random
|
5
10
|
@num_datas = x_datas.shape[0]
|
6
|
-
|
11
|
+
reset_indexs
|
7
12
|
end
|
8
13
|
|
9
|
-
|
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)
|
10
18
|
if @indexes.length < batch_size
|
11
|
-
@indexes
|
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)
|
12
24
|
end
|
13
|
-
batch_indexes = @indexes.shift(batch_size)
|
14
25
|
x_batch = @x_datas[batch_indexes, false]
|
15
26
|
y_batch = @y_datas[batch_indexes, false]
|
16
27
|
[x_batch, y_batch]
|
17
28
|
end
|
29
|
+
|
30
|
+
private def reset_indexs
|
31
|
+
@indexes = @num_datas.times.to_a
|
32
|
+
@indexes.shuffle! if @random
|
33
|
+
end
|
18
34
|
end
|
@@ -28,7 +28,7 @@ module DNN
|
|
28
28
|
class Const < Initializer
|
29
29
|
attr_reader :const
|
30
30
|
|
31
|
-
def self.
|
31
|
+
def self.from_hash(hash)
|
32
32
|
self.new(hash[:const])
|
33
33
|
end
|
34
34
|
|
@@ -51,7 +51,7 @@ module DNN
|
|
51
51
|
attr_reader :mean
|
52
52
|
attr_reader :std
|
53
53
|
|
54
|
-
def self.
|
54
|
+
def self.from_hash(hash)
|
55
55
|
self.new(hash[:mean], hash[:std], hash[:seed])
|
56
56
|
end
|
57
57
|
|
@@ -76,7 +76,7 @@ module DNN
|
|
76
76
|
attr_reader :min
|
77
77
|
attr_reader :max
|
78
78
|
|
79
|
-
def self.
|
79
|
+
def self.from_hash(hash)
|
80
80
|
self.new(hash[:min], hash[:max], hash[:seed])
|
81
81
|
end
|
82
82
|
|
data/lib/dnn/core/layers.rb
CHANGED
@@ -3,6 +3,9 @@ module DNN
|
|
3
3
|
|
4
4
|
# Super class of all optimizer classes.
|
5
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.
|
6
9
|
attr_reader :input_shape
|
7
10
|
|
8
11
|
def initialize
|
@@ -10,12 +13,15 @@ module DNN
|
|
10
13
|
end
|
11
14
|
|
12
15
|
# Build the layer.
|
16
|
+
# @param [Array] input_shape Setting the shape of the input data.
|
13
17
|
def build(input_shape)
|
14
18
|
@input_shape = input_shape
|
19
|
+
@learning_phase = true
|
15
20
|
@built = true
|
16
21
|
end
|
17
22
|
|
18
23
|
# Does the layer have already been built?
|
24
|
+
# @return [Bool] If layer have already been built then return true.
|
19
25
|
def built?
|
20
26
|
@built
|
21
27
|
end
|
@@ -26,10 +32,13 @@ module DNN
|
|
26
32
|
end
|
27
33
|
|
28
34
|
# Backward propagation.
|
29
|
-
def backward(
|
30
|
-
raise NotImplementedError.new("Class '#{self.class.name}' has implement method '
|
35
|
+
def backward(dy)
|
36
|
+
raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'backward'")
|
31
37
|
end
|
32
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.
|
33
42
|
def output_shape
|
34
43
|
@input_shape
|
35
44
|
end
|
@@ -55,31 +64,11 @@ module DNN
|
|
55
64
|
@params = {}
|
56
65
|
@trainable = true
|
57
66
|
end
|
58
|
-
|
59
|
-
def build(input_shape)
|
60
|
-
@input_shape = input_shape
|
61
|
-
unless @built
|
62
|
-
@built = true
|
63
|
-
init_params
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
# Update the parameters.
|
68
|
-
def update(optimizer)
|
69
|
-
optimizer.update(@params) if @trainable
|
70
|
-
end
|
71
|
-
|
72
|
-
private
|
73
|
-
|
74
|
-
# Initialize of the parameters.
|
75
|
-
def init_params
|
76
|
-
raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'init_params'")
|
77
|
-
end
|
78
67
|
end
|
79
68
|
|
80
69
|
|
81
70
|
class InputLayer < Layer
|
82
|
-
def self.
|
71
|
+
def self.from_hash(hash)
|
83
72
|
self.new(hash[:input_shape])
|
84
73
|
end
|
85
74
|
|
@@ -97,8 +86,8 @@ module DNN
|
|
97
86
|
x
|
98
87
|
end
|
99
88
|
|
100
|
-
def backward(
|
101
|
-
|
89
|
+
def backward(dy)
|
90
|
+
dy
|
102
91
|
end
|
103
92
|
|
104
93
|
def to_hash
|
@@ -109,44 +98,42 @@ module DNN
|
|
109
98
|
|
110
99
|
# It is a superclass of all connection layers.
|
111
100
|
class Connection < HasParamLayer
|
112
|
-
# @return [DNN::Initializers]
|
101
|
+
# @return [DNN::Initializers::Initializer] Weight initializer.
|
113
102
|
attr_reader :weight_initializer
|
114
|
-
# @return [DNN::Initializers]
|
103
|
+
# @return [DNN::Initializers::Initializer] Bias initializer.
|
115
104
|
attr_reader :bias_initializer
|
116
|
-
# @return [
|
117
|
-
attr_reader :
|
118
|
-
# @return [
|
119
|
-
attr_reader :
|
120
|
-
|
121
|
-
# @param [DNN::Initializers] weight_initializer
|
122
|
-
# @param [DNN::Initializers] bias_initializer
|
123
|
-
# @param [
|
124
|
-
# @param [
|
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.
|
125
114
|
# @param [Bool] use_bias whether to use bias.
|
126
115
|
def initialize(weight_initializer: Initializers::RandomNormal.new,
|
127
116
|
bias_initializer: Initializers::Zeros.new,
|
128
|
-
|
129
|
-
|
117
|
+
weight_regularizer: nil,
|
118
|
+
bias_regularizer: nil,
|
130
119
|
use_bias: true)
|
131
120
|
super()
|
132
121
|
@weight_initializer = weight_initializer
|
133
122
|
@bias_initializer = bias_initializer
|
134
|
-
@
|
135
|
-
@
|
136
|
-
@params[:weight] = @weight = Param.new
|
137
|
-
|
138
|
-
|
139
|
-
if use_bias || use_bias == nil
|
140
|
-
@params[:bias] = @bias = Param.new
|
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)
|
141
128
|
else
|
142
|
-
@
|
129
|
+
@bias = nil
|
143
130
|
end
|
144
131
|
end
|
145
132
|
|
146
133
|
def regularizers
|
147
134
|
regularizers = []
|
148
|
-
regularizers <<
|
149
|
-
regularizers <<
|
135
|
+
regularizers << @weight_regularizer if @weight_regularizer
|
136
|
+
regularizers << @bias_regularizer if @bias_regularizer
|
150
137
|
regularizers
|
151
138
|
end
|
152
139
|
|
@@ -158,15 +145,18 @@ module DNN
|
|
158
145
|
def to_hash(merge_hash)
|
159
146
|
super({weight_initializer: @weight_initializer.to_hash,
|
160
147
|
bias_initializer: @bias_initializer.to_hash,
|
161
|
-
|
162
|
-
|
148
|
+
weight_regularizer: @weight_regularizer&.to_hash,
|
149
|
+
bias_regularizer: @bias_regularizer&.to_hash,
|
150
|
+
use_bias: use_bias}.merge(merge_hash))
|
163
151
|
end
|
164
152
|
|
165
|
-
private
|
166
|
-
|
167
|
-
def init_params
|
153
|
+
private def init_weight_and_bias
|
168
154
|
@weight_initializer.init_param(self, @weight)
|
169
|
-
@
|
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
|
170
160
|
end
|
171
161
|
end
|
172
162
|
|
@@ -176,12 +166,12 @@ module DNN
|
|
176
166
|
# @return [Integer] number of nodes.
|
177
167
|
attr_reader :num_nodes
|
178
168
|
|
179
|
-
def self.
|
169
|
+
def self.from_hash(hash)
|
180
170
|
self.new(hash[:num_nodes],
|
181
|
-
weight_initializer: Utils.
|
182
|
-
bias_initializer: Utils.
|
183
|
-
|
184
|
-
|
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]),
|
185
175
|
use_bias: hash[:use_bias])
|
186
176
|
end
|
187
177
|
|
@@ -189,25 +179,35 @@ module DNN
|
|
189
179
|
def initialize(num_nodes,
|
190
180
|
weight_initializer: Initializers::RandomNormal.new,
|
191
181
|
bias_initializer: Initializers::Zeros.new,
|
192
|
-
|
193
|
-
|
182
|
+
weight_regularizer: nil,
|
183
|
+
bias_regularizer: nil,
|
194
184
|
use_bias: true)
|
195
185
|
super(weight_initializer: weight_initializer, bias_initializer: bias_initializer,
|
196
|
-
|
186
|
+
weight_regularizer: weight_regularizer, bias_regularizer: bias_regularizer, use_bias: use_bias)
|
197
187
|
@num_nodes = num_nodes
|
198
188
|
end
|
199
|
-
|
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
|
+
|
200
198
|
def forward(x)
|
201
199
|
@x = x
|
202
|
-
|
203
|
-
|
204
|
-
|
200
|
+
y = x.dot(@weight.data)
|
201
|
+
y += @bias.data if @bias
|
202
|
+
y
|
205
203
|
end
|
206
204
|
|
207
|
-
def backward(
|
208
|
-
|
209
|
-
|
210
|
-
|
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
211
|
end
|
212
212
|
|
213
213
|
def output_shape
|
@@ -217,17 +217,6 @@ module DNN
|
|
217
217
|
def to_hash
|
218
218
|
super({num_nodes: @num_nodes})
|
219
219
|
end
|
220
|
-
|
221
|
-
private
|
222
|
-
|
223
|
-
# TODO
|
224
|
-
# Change writing super() other than the first.
|
225
|
-
def init_params
|
226
|
-
num_prev_nodes = @input_shape[0]
|
227
|
-
@weight.data = Xumo::SFloat.new(num_prev_nodes, @num_nodes)
|
228
|
-
@bias.data = Xumo::SFloat.new(@num_nodes) if @bias
|
229
|
-
super()
|
230
|
-
end
|
231
220
|
end
|
232
221
|
|
233
222
|
|
@@ -236,8 +225,8 @@ module DNN
|
|
236
225
|
x.reshape(x.shape[0], *output_shape)
|
237
226
|
end
|
238
227
|
|
239
|
-
def backward(
|
240
|
-
|
228
|
+
def backward(dy)
|
229
|
+
dy.reshape(dy.shape[0], *@input_shape)
|
241
230
|
end
|
242
231
|
|
243
232
|
def output_shape
|
@@ -247,7 +236,7 @@ module DNN
|
|
247
236
|
|
248
237
|
|
249
238
|
class Reshape < Layer
|
250
|
-
def self.
|
239
|
+
def self.from_hash(hash)
|
251
240
|
self.new(hash[:output_shape])
|
252
241
|
end
|
253
242
|
|
@@ -260,8 +249,8 @@ module DNN
|
|
260
249
|
x.reshape(x.shape[0], *@output_shape)
|
261
250
|
end
|
262
251
|
|
263
|
-
def backward(
|
264
|
-
|
252
|
+
def backward(dy)
|
253
|
+
dy.reshape(dy.shape[0], *@input_shape)
|
265
254
|
end
|
266
255
|
|
267
256
|
def output_shape
|
@@ -276,11 +265,11 @@ module DNN
|
|
276
265
|
|
277
266
|
class Dropout < Layer
|
278
267
|
# @return [Float] dropout ratio.
|
279
|
-
|
268
|
+
attr_accessor :dropout_ratio
|
280
269
|
# @return [Float] Use 'weight scaling inference rule'.
|
281
270
|
attr_reader :use_scale
|
282
271
|
|
283
|
-
def self.
|
272
|
+
def self.from_hash(hash)
|
284
273
|
self.new(hash[:dropout_ratio], seed: hash[:seed], use_scale: hash[:use_scale])
|
285
274
|
end
|
286
275
|
|
@@ -292,7 +281,7 @@ module DNN
|
|
292
281
|
@mask = nil
|
293
282
|
end
|
294
283
|
|
295
|
-
def forward(x
|
284
|
+
def forward(x)
|
296
285
|
if learning_phase
|
297
286
|
Xumo::SFloat.srand(@seed)
|
298
287
|
@mask = Xumo::SFloat.ones(*x.shape).rand < @dropout_ratio
|
@@ -303,9 +292,9 @@ module DNN
|
|
303
292
|
x
|
304
293
|
end
|
305
294
|
|
306
|
-
def backward(
|
307
|
-
|
308
|
-
|
295
|
+
def backward(dy)
|
296
|
+
dy[@mask] = 0
|
297
|
+
dy
|
309
298
|
end
|
310
299
|
|
311
300
|
def to_hash
|
@@ -313,64 +302,6 @@ module DNN
|
|
313
302
|
end
|
314
303
|
end
|
315
304
|
|
316
|
-
|
317
|
-
class BatchNormalization < HasParamLayer
|
318
|
-
# @return [Float] Exponential moving average of mean and variance.
|
319
|
-
attr_reader :momentum
|
320
|
-
|
321
|
-
def self.load_hash(hash)
|
322
|
-
self.new(momentum: hash[:momentum])
|
323
|
-
end
|
324
|
-
|
325
|
-
# @param [Float] momentum Exponential moving average of mean and variance.
|
326
|
-
def initialize(momentum: 0.9)
|
327
|
-
super()
|
328
|
-
@momentum = momentum
|
329
|
-
end
|
330
|
-
|
331
|
-
def forward(x, learning_phase)
|
332
|
-
if learning_phase
|
333
|
-
mean = x.mean(0)
|
334
|
-
@xc = x - mean
|
335
|
-
var = (@xc**2).mean(0)
|
336
|
-
@std = NMath.sqrt(var + 1e-7)
|
337
|
-
xn = @xc / @std
|
338
|
-
@xn = xn
|
339
|
-
@running_mean.data = @momentum * @running_mean.data + (1 - @momentum) * mean
|
340
|
-
@running_var.data = @momentum * @running_var.data + (1 - @momentum) * var
|
341
|
-
else
|
342
|
-
xc = x - @running_mean.data
|
343
|
-
xn = xc / NMath.sqrt(@running_var.data + 1e-7)
|
344
|
-
end
|
345
|
-
@gamma.data * xn + @beta.data
|
346
|
-
end
|
347
|
-
|
348
|
-
def backward(dout)
|
349
|
-
batch_size = dout.shape[0]
|
350
|
-
@beta.grad = dout.sum(0)
|
351
|
-
@gamma.grad = (@xn * dout).sum(0)
|
352
|
-
dxn = @gamma.data * dout
|
353
|
-
dxc = dxn / @std
|
354
|
-
dstd = -((dxn * @xc) / (@std**2)).sum(0)
|
355
|
-
dvar = 0.5 * dstd / @std
|
356
|
-
dxc += (2.0 / batch_size) * @xc * dvar
|
357
|
-
dmean = dxc.sum(0)
|
358
|
-
dxc - dmean / batch_size
|
359
|
-
end
|
360
|
-
|
361
|
-
def to_hash
|
362
|
-
super({momentum: @momentum})
|
363
|
-
end
|
364
|
-
|
365
|
-
private
|
366
|
-
|
367
|
-
def init_params
|
368
|
-
@params[:gamma] = @gamma = Param.new(Xumo::SFloat.ones(*output_shape))
|
369
|
-
@params[:beta] = @beta = Param.new(Xumo::SFloat.zeros(*output_shape))
|
370
|
-
@params[:running_mean] = @running_mean = Param.new(Xumo::SFloat.zeros(*output_shape))
|
371
|
-
@params[:running_var] = @running_var = Param.new(Xumo::SFloat.zeros(*output_shape))
|
372
|
-
end
|
373
|
-
end
|
374
305
|
end
|
375
306
|
|
376
307
|
end
|
data/lib/dnn/core/losses.rb
CHANGED
@@ -2,8 +2,8 @@ module DNN
|
|
2
2
|
module Losses
|
3
3
|
|
4
4
|
class Loss
|
5
|
-
def forward(
|
6
|
-
loss_value =
|
5
|
+
def forward(x, y, layers)
|
6
|
+
loss_value = forward_loss(x, y)
|
7
7
|
regularizers = layers.select { |layer| layer.is_a?(Connection) }
|
8
8
|
.map { |layer| layer.regularizers }.flatten
|
9
9
|
|
@@ -13,122 +13,161 @@ module DNN
|
|
13
13
|
loss_value
|
14
14
|
end
|
15
15
|
|
16
|
-
def backward(y)
|
17
|
-
raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'backward'")
|
18
|
-
end
|
19
|
-
|
20
|
-
def regularizes_backward(layers)
|
16
|
+
def backward(y, layers)
|
21
17
|
layers.select { |layer| layer.is_a?(Connection) }.each do |layer|
|
22
18
|
layer.regularizers.each do |regularizer|
|
23
19
|
regularizer.backward
|
24
20
|
end
|
25
21
|
end
|
22
|
+
backward_loss(y)
|
26
23
|
end
|
27
24
|
|
28
|
-
def to_hash
|
29
|
-
{class: self.class.name}
|
25
|
+
def to_hash(merge_hash = nil)
|
26
|
+
hash = {class: self.class.name}
|
27
|
+
hash.merge!(merge_hash) if merge_hash
|
28
|
+
hash
|
30
29
|
end
|
31
30
|
|
32
31
|
private
|
33
32
|
|
34
|
-
def
|
35
|
-
raise NotImplementedError.new("Class '#{self.class.name}' has implement method '
|
33
|
+
def forward_loss(x, y)
|
34
|
+
raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'forward_loss'")
|
35
|
+
end
|
36
|
+
|
37
|
+
def backward_loss(y)
|
38
|
+
raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'backward_loss'")
|
36
39
|
end
|
37
40
|
end
|
38
41
|
|
39
42
|
class MeanSquaredError < Loss
|
40
|
-
|
41
|
-
|
43
|
+
private
|
44
|
+
|
45
|
+
def forward_loss(x, y)
|
46
|
+
@x = x
|
42
47
|
batch_size = y.shape[0]
|
43
|
-
0.5 * ((
|
48
|
+
0.5 * ((x - y)**2).sum / batch_size
|
44
49
|
end
|
45
50
|
|
46
|
-
def
|
47
|
-
@
|
51
|
+
def backward_loss(y)
|
52
|
+
@x - y
|
48
53
|
end
|
49
54
|
end
|
50
55
|
|
51
56
|
|
52
57
|
class MeanAbsoluteError < Loss
|
53
|
-
|
54
|
-
|
58
|
+
private
|
59
|
+
|
60
|
+
def forward_loss(x, y)
|
61
|
+
@x = x
|
55
62
|
batch_size = y.shape[0]
|
56
|
-
(
|
63
|
+
(x - y).abs.sum / batch_size
|
57
64
|
end
|
58
65
|
|
59
|
-
def
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
66
|
+
def backward_loss(y)
|
67
|
+
dy = @x - y
|
68
|
+
dy[dy >= 0] = 1
|
69
|
+
dy[dy < 0] = -1
|
70
|
+
dy
|
64
71
|
end
|
65
72
|
end
|
66
73
|
|
67
74
|
|
68
75
|
class HuberLoss < Loss
|
69
|
-
def forward(
|
70
|
-
@loss_value = super(
|
76
|
+
def forward(x, y, layers)
|
77
|
+
@loss_value = super(x, y, layers)
|
71
78
|
end
|
72
79
|
|
73
|
-
|
74
|
-
|
80
|
+
private
|
81
|
+
|
82
|
+
def forward_loss(x, y)
|
83
|
+
@x = x
|
75
84
|
loss_value = loss_l1(y)
|
76
85
|
loss_value > 1 ? loss_value : loss_l2(y)
|
77
86
|
end
|
78
87
|
|
79
|
-
def
|
80
|
-
|
88
|
+
def backward_loss(y)
|
89
|
+
dy = @x - y
|
81
90
|
if @loss_value > 1
|
82
|
-
|
83
|
-
|
91
|
+
dy[dy >= 0] = 1
|
92
|
+
dy[dy < 0] = -1
|
84
93
|
end
|
85
|
-
|
94
|
+
dy
|
86
95
|
end
|
87
96
|
|
88
|
-
private
|
89
|
-
|
90
97
|
def loss_l1(y)
|
91
98
|
batch_size = y.shape[0]
|
92
|
-
(@
|
99
|
+
(@x - y).abs.sum / batch_size
|
93
100
|
end
|
94
101
|
|
95
102
|
def loss_l2(y)
|
96
103
|
batch_size = y.shape[0]
|
97
|
-
0.5 * ((@
|
104
|
+
0.5 * ((@x - y)**2).sum / batch_size
|
98
105
|
end
|
99
106
|
end
|
100
107
|
|
101
108
|
|
102
109
|
class SoftmaxCrossEntropy < Loss
|
110
|
+
# @return [Float] Return the eps value.
|
111
|
+
attr_accessor :eps
|
112
|
+
|
113
|
+
def self.from_hash(hash)
|
114
|
+
SoftmaxCrossEntropy.new(eps: hash[:eps])
|
115
|
+
end
|
116
|
+
|
103
117
|
def self.softmax(x)
|
104
118
|
NMath.exp(x) / NMath.exp(x).sum(1).reshape(x.shape[0], 1)
|
105
119
|
end
|
106
120
|
|
107
|
-
|
108
|
-
|
121
|
+
# @param [Float] eps Value to avoid nan.
|
122
|
+
def initialize(eps: 1e-7)
|
123
|
+
@eps = eps
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_hash
|
127
|
+
super(eps: @eps)
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def forward_loss(x, y)
|
133
|
+
@x = SoftmaxCrossEntropy.softmax(x)
|
109
134
|
batch_size = y.shape[0]
|
110
|
-
-(y * NMath.log(@
|
135
|
+
-(y * NMath.log(@x + @eps)).sum / batch_size
|
111
136
|
end
|
112
137
|
|
113
|
-
def
|
114
|
-
@
|
138
|
+
def backward_loss(y)
|
139
|
+
@x - y
|
115
140
|
end
|
116
141
|
end
|
117
142
|
|
118
143
|
|
119
144
|
class SigmoidCrossEntropy < Loss
|
120
|
-
|
121
|
-
|
145
|
+
# @return [Float] Return the eps value.
|
146
|
+
attr_accessor :eps
|
147
|
+
|
148
|
+
def self.from_hash(hash)
|
149
|
+
SigmoidCrossEntropy.new(eps: hash[:eps])
|
122
150
|
end
|
123
151
|
|
124
|
-
|
125
|
-
|
152
|
+
# @param [Float] eps Value to avoid nan.
|
153
|
+
def initialize(eps: 1e-7)
|
154
|
+
@eps = eps
|
155
|
+
end
|
156
|
+
|
157
|
+
def to_hash
|
158
|
+
super(eps: @eps)
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
def forward_loss(x, y)
|
164
|
+
@x = Sigmoid.new.forward(x)
|
126
165
|
batch_size = y.shape[0]
|
127
|
-
-(y * NMath.log(@
|
166
|
+
-(y * NMath.log(@x) + (1 - y) * NMath.log(1 - @x))
|
128
167
|
end
|
129
168
|
|
130
|
-
def
|
131
|
-
@
|
169
|
+
def backward_loss(y)
|
170
|
+
@x - y
|
132
171
|
end
|
133
172
|
end
|
134
173
|
|