ruby-dnn 0.9.3 → 0.9.4
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/examples/iris_example.rb +33 -0
- data/lib/dnn/core/activations.rb +1 -1
- data/lib/dnn/core/cnn_layers.rb +24 -13
- data/lib/dnn/core/layers.rb +28 -11
- data/lib/dnn/core/losses.rb +11 -3
- data/lib/dnn/core/model.rb +8 -5
- data/lib/dnn/core/rnn_layers.rb +42 -28
- data/lib/dnn/core/utils.rb +4 -4
- data/lib/dnn/lib/iris.rb +60 -0
- data/lib/dnn/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 670d2d681e3929d20c5c855c97dbad1759560eb45018d20a9089c6bc62787833
|
4
|
+
data.tar.gz: a9360305a97aed1bac5300010fbfa3ea85565eab70d299a9d07acd78d6b24d63
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1f3b49241a81bf8c56b595bdaeecf097d8d3d461a02ab37860c6d2eede4a7e5cfadd0f83d2759cdbbdc7d541bbadebd11e55a80b0cfce9c2bd8a216efb2596d
|
7
|
+
data.tar.gz: e92230987d4c59cf4395ee33695fc974c65940f7df6aecfc3f89ebf65bbbeaae95433079df8855c448850923b5d230807eca2d2c135d475df6aa1aba6431988a
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "dnn"
|
2
|
+
require "dnn/lib/iris"
|
3
|
+
# require "numo/linalg/autoloader"
|
4
|
+
|
5
|
+
include DNN::Layers
|
6
|
+
include DNN::Activations
|
7
|
+
include DNN::Optimizers
|
8
|
+
include DNN::Losses
|
9
|
+
Model = DNN::Model
|
10
|
+
Iris = DNN::Iris
|
11
|
+
|
12
|
+
x, y = Iris.load(true)
|
13
|
+
x_train, y_train = x[0...100, true], y[0...100]
|
14
|
+
x_test, y_test = x[100...150, true], y[100...150]
|
15
|
+
|
16
|
+
x_train /= 255
|
17
|
+
x_test /= 255
|
18
|
+
|
19
|
+
y_train = DNN::Utils.to_categorical(y_train, 3, Numo::SFloat)
|
20
|
+
y_test = DNN::Utils.to_categorical(y_test, 3, Numo::SFloat)
|
21
|
+
|
22
|
+
model = Model.new
|
23
|
+
|
24
|
+
model << InputLayer.new(4)
|
25
|
+
|
26
|
+
model << Dense.new(64)
|
27
|
+
model << ReLU.new
|
28
|
+
|
29
|
+
model << Dense.new(3)
|
30
|
+
|
31
|
+
model.compile(Adam.new, SoftmaxCrossEntropy.new)
|
32
|
+
|
33
|
+
model.train(x_train, y_train, 1000, batch_size: 10, test: [x_test, y_test])
|
data/lib/dnn/core/activations.rb
CHANGED
data/lib/dnn/core/cnn_layers.rb
CHANGED
@@ -4,7 +4,7 @@ module DNN
|
|
4
4
|
module Conv2DModule
|
5
5
|
private
|
6
6
|
|
7
|
-
# img[bsize, out_h, out_w,
|
7
|
+
# img[bsize, out_h, out_w, ch] to col[bsize * out_h * out_w, fil_h * fil_w * ch]
|
8
8
|
def im2col(img, out_h, out_w, fil_h, fil_w, strides)
|
9
9
|
bsize = img.shape[0]
|
10
10
|
ch = img.shape[3]
|
@@ -19,7 +19,7 @@ module DNN
|
|
19
19
|
col.reshape(bsize * out_h * out_w, fil_h * fil_w * ch)
|
20
20
|
end
|
21
21
|
|
22
|
-
# col[bsize * out_h * out_w, fil_h * fil_w * ch] to img[bsize, out_h, out_w,
|
22
|
+
# col[bsize * out_h * out_w, fil_h * fil_w * ch] to img[bsize, out_h, out_w, ch]
|
23
23
|
def col2im(col, img_shape, out_h, out_w, fil_h, fil_w, strides)
|
24
24
|
bsize, img_h, img_w, ch = img_shape
|
25
25
|
col = col.reshape(bsize, out_h, out_w, fil_h, fil_w, ch)
|
@@ -84,7 +84,8 @@ module DNN
|
|
84
84
|
strides: hash[:strides],
|
85
85
|
padding: hash[:padding],
|
86
86
|
l1_lambda: hash[:l1_lambda],
|
87
|
-
l2_lambda: hash[:l2_lambda]
|
87
|
+
l2_lambda: hash[:l2_lambda],
|
88
|
+
use_bias: hash[:use_bias])
|
88
89
|
end
|
89
90
|
|
90
91
|
# @param [Integer] num_filters number of filters.
|
@@ -93,13 +94,14 @@ module DNN
|
|
93
94
|
# @param [Bool] padding Whether to padding.
|
94
95
|
def initialize(num_filters, filter_size,
|
95
96
|
weight_initializer: Initializers::RandomNormal.new,
|
96
|
-
bias_initializer: Initializers::
|
97
|
+
bias_initializer: Initializers::Zeros.new,
|
97
98
|
strides: 1,
|
98
99
|
padding: false,
|
99
100
|
l1_lambda: 0,
|
100
|
-
l2_lambda: 0
|
101
|
+
l2_lambda: 0,
|
102
|
+
use_bias: true)
|
101
103
|
super(weight_initializer: weight_initializer, bias_initializer: bias_initializer,
|
102
|
-
l1_lambda: l1_lambda, l2_lambda: l2_lambda)
|
104
|
+
l1_lambda: l1_lambda, l2_lambda: l2_lambda, use_bias: use_bias)
|
103
105
|
@num_filters = num_filters
|
104
106
|
@filter_size = filter_size.is_a?(Integer) ? [filter_size, filter_size] : filter_size
|
105
107
|
@strides = strides.is_a?(Integer) ? [strides, strides] : strides
|
@@ -120,14 +122,15 @@ module DNN
|
|
120
122
|
x = padding(x, @pad_size) if @padding
|
121
123
|
@x_shape = x.shape
|
122
124
|
@col = im2col(x, *@out_size, *@filter_size, @strides)
|
123
|
-
out = @col.dot(@weight.data)
|
125
|
+
out = @col.dot(@weight.data)
|
126
|
+
out += @bias.data if @bias
|
124
127
|
out.reshape(x.shape[0], *@out_size, out.shape[3])
|
125
128
|
end
|
126
129
|
|
127
130
|
def backward(dout)
|
128
131
|
dout = dout.reshape(dout.shape[0..2].reduce(:*), dout.shape[3])
|
129
132
|
@weight.grad = @col.transpose.dot(dout)
|
130
|
-
@bias.grad = dout.sum(0)
|
133
|
+
@bias.grad = dout.sum(0) if @bias
|
131
134
|
dcol = dout.dot(@weight.data.transpose)
|
132
135
|
dx = col2im(dcol, @x_shape, *@out_size, *@filter_size, @strides)
|
133
136
|
@padding ? back_padding(dx, @pad_size) : dx
|
@@ -166,7 +169,7 @@ module DNN
|
|
166
169
|
def init_params
|
167
170
|
num_prev_filter = @input_shape[2]
|
168
171
|
@weight.data = Xumo::SFloat.new(@filter_size.reduce(:*) * num_prev_filter, @num_filters)
|
169
|
-
@bias.data = Xumo::SFloat.new(@num_filters)
|
172
|
+
@bias.data = Xumo::SFloat.new(@num_filters) if @bias
|
170
173
|
super()
|
171
174
|
end
|
172
175
|
end
|
@@ -305,18 +308,26 @@ module DNN
|
|
305
308
|
@num_channel = input_shape[2]
|
306
309
|
end
|
307
310
|
|
311
|
+
include Conv2DModule
|
312
|
+
|
308
313
|
def forward(x)
|
309
314
|
@x_shape = x.shape
|
310
315
|
unpool_h, unpool_w = @unpool_size
|
311
316
|
x2 = Xumo::SFloat.zeros(x.shape[0], x.shape[1], unpool_h, x.shape[2], unpool_w, @num_channel)
|
312
|
-
|
317
|
+
unpool_h.times do |i|
|
318
|
+
unpool_w.times do |j|
|
319
|
+
x2[true, true, i, true, j, true] = x
|
320
|
+
end
|
321
|
+
end
|
313
322
|
x2.reshape(x.shape[0], *@out_size, x.shape[3])
|
314
323
|
end
|
315
324
|
|
316
325
|
def backward(dout)
|
317
|
-
|
318
|
-
|
319
|
-
dout[
|
326
|
+
in_size = input_shape[0..1]
|
327
|
+
col = im2col(dout, *input_shape[0..1], *@unpool_size, @unpool_size)
|
328
|
+
col = col.reshape(dout.shape[0] * in_size.reduce(:*), @unpool_size.reduce(:*), dout.shape[3]).transpose(0, 2, 1)
|
329
|
+
.reshape(dout.shape[0] * in_size.reduce(:*) * dout.shape[3], @unpool_size.reduce(:*))
|
330
|
+
col.sum(1).reshape(dout.shape[0], *in_size, dout.shape[3])
|
320
331
|
end
|
321
332
|
|
322
333
|
def output_shape
|
data/lib/dnn/core/layers.rb
CHANGED
@@ -113,26 +113,34 @@ module DNN
|
|
113
113
|
attr_reader :weight_initializer
|
114
114
|
# @return [DNN::Initializers] bias initializer.
|
115
115
|
attr_reader :bias_initializer
|
116
|
-
# @return [Float] L1 regularization
|
116
|
+
# @return [Float] L1 regularization.
|
117
117
|
attr_reader :l1_lambda
|
118
|
-
# @return [Float] L2 regularization
|
118
|
+
# @return [Float] L2 regularization.
|
119
119
|
attr_reader :l2_lambda
|
120
120
|
|
121
121
|
# @param [DNN::Initializers] weight_initializer weight initializer.
|
122
122
|
# @param [DNN::Initializers] bias_initializer bias initializer.
|
123
123
|
# @param [Float] l1_lambda L1 regularization
|
124
124
|
# @param [Float] l2_lambda L2 regularization
|
125
|
+
# @param [Bool] use_bias whether to use bias.
|
125
126
|
def initialize(weight_initializer: Initializers::RandomNormal.new,
|
126
127
|
bias_initializer: Initializers::Zeros.new,
|
127
128
|
l1_lambda: 0,
|
128
|
-
l2_lambda: 0
|
129
|
+
l2_lambda: 0,
|
130
|
+
use_bias: true)
|
129
131
|
super()
|
130
132
|
@weight_initializer = weight_initializer
|
131
133
|
@bias_initializer = bias_initializer
|
132
134
|
@l1_lambda = l1_lambda
|
133
135
|
@l2_lambda = l2_lambda
|
134
136
|
@params[:weight] = @weight = Param.new
|
135
|
-
|
137
|
+
# For compatibility on or before with v0.9.3, setting use_bias to nil use bias.
|
138
|
+
# Therefore, setting use_bias to nil is deprecated.
|
139
|
+
if use_bias || use_bias == nil
|
140
|
+
@params[:bias] = @bias = Param.new
|
141
|
+
else
|
142
|
+
@params[:bias] = @bias = nil
|
143
|
+
end
|
136
144
|
end
|
137
145
|
|
138
146
|
def regularizers
|
@@ -142,6 +150,11 @@ module DNN
|
|
142
150
|
regularizers
|
143
151
|
end
|
144
152
|
|
153
|
+
# @return [Bool] Return whether to use bias.
|
154
|
+
def use_bias
|
155
|
+
@bias ? true : false
|
156
|
+
end
|
157
|
+
|
145
158
|
def to_hash(merge_hash)
|
146
159
|
super({weight_initializer: @weight_initializer.to_hash,
|
147
160
|
bias_initializer: @bias_initializer.to_hash,
|
@@ -153,7 +166,7 @@ module DNN
|
|
153
166
|
|
154
167
|
def init_params
|
155
168
|
@weight_initializer.init_param(self, @weight)
|
156
|
-
@bias_initializer.init_param(self, @bias)
|
169
|
+
@bias_initializer.init_param(self, @bias) if @bias
|
157
170
|
end
|
158
171
|
end
|
159
172
|
|
@@ -168,7 +181,8 @@ module DNN
|
|
168
181
|
weight_initializer: Utils.load_hash(hash[:weight_initializer]),
|
169
182
|
bias_initializer: Utils.load_hash(hash[:bias_initializer]),
|
170
183
|
l1_lambda: hash[:l1_lambda],
|
171
|
-
l2_lambda: hash[:l2_lambda]
|
184
|
+
l2_lambda: hash[:l2_lambda],
|
185
|
+
use_bias: hash[:use_bias])
|
172
186
|
end
|
173
187
|
|
174
188
|
# @param [Integer] num_nodes number of nodes.
|
@@ -176,20 +190,23 @@ module DNN
|
|
176
190
|
weight_initializer: Initializers::RandomNormal.new,
|
177
191
|
bias_initializer: Initializers::Zeros.new,
|
178
192
|
l1_lambda: 0,
|
179
|
-
l2_lambda: 0
|
193
|
+
l2_lambda: 0,
|
194
|
+
use_bias: true)
|
180
195
|
super(weight_initializer: weight_initializer, bias_initializer: bias_initializer,
|
181
|
-
l1_lambda: l1_lambda, l2_lambda: l2_lambda)
|
196
|
+
l1_lambda: l1_lambda, l2_lambda: l2_lambda, use_bias: use_bias)
|
182
197
|
@num_nodes = num_nodes
|
183
198
|
end
|
184
199
|
|
185
200
|
def forward(x)
|
186
201
|
@x = x
|
187
|
-
|
202
|
+
out = x.dot(@weight.data)
|
203
|
+
out += @bias.data if @bias
|
204
|
+
out
|
188
205
|
end
|
189
206
|
|
190
207
|
def backward(dout)
|
191
208
|
@weight.grad = @x.transpose.dot(dout)
|
192
|
-
@bias.grad = dout.sum(0)
|
209
|
+
@bias.grad = dout.sum(0) if @bias
|
193
210
|
dout.dot(@weight.data.transpose)
|
194
211
|
end
|
195
212
|
|
@@ -208,7 +225,7 @@ module DNN
|
|
208
225
|
def init_params
|
209
226
|
num_prev_nodes = @input_shape[0]
|
210
227
|
@weight.data = Xumo::SFloat.new(num_prev_nodes, @num_nodes)
|
211
|
-
@bias.data = Xumo::SFloat.new(@num_nodes)
|
228
|
+
@bias.data = Xumo::SFloat.new(@num_nodes) if @bias
|
212
229
|
super()
|
213
230
|
end
|
214
231
|
end
|
data/lib/dnn/core/losses.rb
CHANGED
@@ -100,8 +100,12 @@ module DNN
|
|
100
100
|
|
101
101
|
|
102
102
|
class SoftmaxCrossEntropy < Loss
|
103
|
+
def self.softmax(x)
|
104
|
+
NMath.exp(x) / NMath.exp(x).sum(1).reshape(x.shape[0], 1)
|
105
|
+
end
|
106
|
+
|
103
107
|
def loss(x, y)
|
104
|
-
@out =
|
108
|
+
@out = SoftmaxCrossEntropy.softmax(x)
|
105
109
|
batch_size = y.shape[0]
|
106
110
|
-(y * NMath.log(@out + 1e-7)).sum / batch_size
|
107
111
|
end
|
@@ -113,10 +117,14 @@ module DNN
|
|
113
117
|
|
114
118
|
|
115
119
|
class SigmoidCrossEntropy < Loss
|
120
|
+
def initialize
|
121
|
+
@sigmoid = Sigmoid.new
|
122
|
+
end
|
123
|
+
|
116
124
|
def loss(x, y)
|
117
|
-
@out =
|
125
|
+
@out = @sigmoid.forward(x)
|
118
126
|
batch_size = y.shape[0]
|
119
|
-
-(y * NMath.log(@out + 1e-7) + (1 - y) * NMath.log(1 - @out + 1e-7))
|
127
|
+
-(y * NMath.log(@out + 1e-7) + (1 - y) * NMath.log(1 - @out + 1e-7))
|
120
128
|
end
|
121
129
|
|
122
130
|
def backward(y)
|
data/lib/dnn/core/model.rb
CHANGED
@@ -213,8 +213,10 @@ module DNN
|
|
213
213
|
puts "【 epoch #{epoch}/#{epochs} 】" if verbose
|
214
214
|
(num_train_datas.to_f / batch_size).ceil.times do |index|
|
215
215
|
x_batch, y_batch = dataset.get_batch(batch_size)
|
216
|
-
|
217
|
-
if
|
216
|
+
loss_value = train_on_batch(x_batch, y_batch, &batch_proc)
|
217
|
+
if loss_value.is_a?(Numo::SFloat)
|
218
|
+
loss_value = loss_value.mean
|
219
|
+
elsif loss_value.nan?
|
218
220
|
puts "\nloss is nan" if verbose
|
219
221
|
return
|
220
222
|
end
|
@@ -230,7 +232,7 @@ module DNN
|
|
230
232
|
log << "_"
|
231
233
|
end
|
232
234
|
end
|
233
|
-
log << " #{num_trained_datas}/#{num_train_datas} loss: #{sprintf('%.8f',
|
235
|
+
log << " #{num_trained_datas}/#{num_train_datas} loss: #{sprintf('%.8f', loss_value)}"
|
234
236
|
print log if verbose
|
235
237
|
end
|
236
238
|
if verbose && test
|
@@ -246,6 +248,7 @@ module DNN
|
|
246
248
|
# Compile the model before use this method.
|
247
249
|
# @param [Numo::SFloat] x Input training data.
|
248
250
|
# @param [Numo::SFloat] y Output training data.
|
251
|
+
# @return [Float | Numo::SFloat] Return loss value in the form of Float or Numo::SFloat.
|
249
252
|
# @yield [x, y] batch_proc Set proc to process per batch.
|
250
253
|
def train_on_batch(x, y, &batch_proc)
|
251
254
|
raise DNN_Error.new("The model is not compiled.") unless compiled?
|
@@ -304,7 +307,7 @@ module DNN
|
|
304
307
|
# @param [Numo::SFloat] x Input data. However, x is single data.
|
305
308
|
def predict1(x)
|
306
309
|
check_xy_type(x)
|
307
|
-
predict(
|
310
|
+
predict(x.reshape(1, *x.shape))[0, false]
|
308
311
|
end
|
309
312
|
|
310
313
|
# @return [DNN::Model] Copy this model.
|
@@ -333,7 +336,7 @@ module DNN
|
|
333
336
|
|
334
337
|
# TODO
|
335
338
|
# It is not good to write the Layer class name directly in the Model class. I will fix it later.
|
336
|
-
def forward(x, learning_phase)
|
339
|
+
def forward(x, learning_phase)
|
337
340
|
@layers.each do |layer|
|
338
341
|
x = if layer.is_a?(Layers::Dropout) || layer.is_a?(Layers::BatchNormalization) || layer.is_a?(Model)
|
339
342
|
layer.forward(x, learning_phase)
|
data/lib/dnn/core/rnn_layers.rb
CHANGED
@@ -18,9 +18,10 @@ module DNN
|
|
18
18
|
weight_initializer: RandomNormal.new,
|
19
19
|
bias_initializer: Zeros.new,
|
20
20
|
l1_lambda: 0,
|
21
|
-
l2_lambda: 0
|
21
|
+
l2_lambda: 0,
|
22
|
+
use_bias: true)
|
22
23
|
super(weight_initializer: weight_initializer, bias_initializer: bias_initializer,
|
23
|
-
l1_lambda: l1_lambda, l2_lambda: l2_lambda)
|
24
|
+
l1_lambda: l1_lambda, l2_lambda: l2_lambda, use_bias: use_bias)
|
24
25
|
@num_nodes = num_nodes
|
25
26
|
@stateful = stateful
|
26
27
|
@return_sequences = return_sequences
|
@@ -47,7 +48,7 @@ module DNN
|
|
47
48
|
def backward(dh2s)
|
48
49
|
@weight.grad = Xumo::SFloat.zeros(*@weight.data.shape)
|
49
50
|
@weight2.grad = Xumo::SFloat.zeros(*@weight2.data.shape)
|
50
|
-
@bias.grad = Xumo::SFloat.zeros(*@bias.data.shape)
|
51
|
+
@bias.grad = Xumo::SFloat.zeros(*@bias.data.shape) if @bias
|
51
52
|
unless @return_sequences
|
52
53
|
dh = dh2s
|
53
54
|
dh2s = Xumo::SFloat.zeros(dh.shape[0], @time_length, dh.shape[1])
|
@@ -114,7 +115,8 @@ module DNN
|
|
114
115
|
def forward(x, h)
|
115
116
|
@x = x
|
116
117
|
@h = h
|
117
|
-
h2 = x.dot(@weight.data) + h.dot(@weight2.data)
|
118
|
+
h2 = x.dot(@weight.data) + h.dot(@weight2.data)
|
119
|
+
h2 += @bias.data if @bias
|
118
120
|
@activation.forward(h2)
|
119
121
|
end
|
120
122
|
|
@@ -122,7 +124,7 @@ module DNN
|
|
122
124
|
dh2 = @activation.backward(dh2)
|
123
125
|
@weight.grad += @x.transpose.dot(dh2)
|
124
126
|
@weight2.grad += @h.transpose.dot(dh2)
|
125
|
-
@bias.grad += dh2.sum(0)
|
127
|
+
@bias.grad += dh2.sum(0) if @bias
|
126
128
|
dx = dh2.dot(@weight.data.transpose)
|
127
129
|
dh = dh2.dot(@weight2.data.transpose)
|
128
130
|
[dx, dh]
|
@@ -143,7 +145,8 @@ module DNN
|
|
143
145
|
weight_initializer: Utils.load_hash(hash[:weight_initializer]),
|
144
146
|
bias_initializer: Utils.load_hash(hash[:bias_initializer]),
|
145
147
|
l1_lambda: hash[:l1_lambda],
|
146
|
-
l2_lambda: hash[:l2_lambda]
|
148
|
+
l2_lambda: hash[:l2_lambda],
|
149
|
+
use_bias: hash[:use_bias])
|
147
150
|
simple_rnn
|
148
151
|
end
|
149
152
|
|
@@ -154,14 +157,16 @@ module DNN
|
|
154
157
|
weight_initializer: RandomNormal.new,
|
155
158
|
bias_initializer: Zeros.new,
|
156
159
|
l1_lambda: 0,
|
157
|
-
l2_lambda: 0
|
160
|
+
l2_lambda: 0,
|
161
|
+
use_bias: true)
|
158
162
|
super(num_nodes,
|
159
163
|
stateful: stateful,
|
160
164
|
return_sequences: return_sequences,
|
161
165
|
weight_initializer: weight_initializer,
|
162
166
|
bias_initializer: bias_initializer,
|
163
167
|
l1_lambda: l1_lambda,
|
164
|
-
l2_lambda: l2_lambda
|
168
|
+
l2_lambda: l2_lambda,
|
169
|
+
use_bias: use_bias)
|
165
170
|
@activation = activation
|
166
171
|
end
|
167
172
|
|
@@ -176,10 +181,10 @@ module DNN
|
|
176
181
|
num_prev_nodes = @input_shape[1]
|
177
182
|
@weight.data = Xumo::SFloat.new(num_prev_nodes, @num_nodes)
|
178
183
|
@weight2.data = Xumo::SFloat.new(@num_nodes, @num_nodes)
|
179
|
-
@bias.data = Xumo::SFloat.new(@num_nodes)
|
184
|
+
@bias.data = Xumo::SFloat.new(@num_nodes) if @bias
|
180
185
|
@weight_initializer.init_param(self, @weight)
|
181
186
|
@weight_initializer.init_param(self, @weight2)
|
182
|
-
@bias_initializer.init_param(self, @bias)
|
187
|
+
@bias_initializer.init_param(self, @bias) if @bias
|
183
188
|
@time_length.times do |t|
|
184
189
|
@layers << SimpleRNN_Dense.new(@weight, @weight2, @bias, @activation)
|
185
190
|
end
|
@@ -204,7 +209,8 @@ module DNN
|
|
204
209
|
@h = h
|
205
210
|
@c = c
|
206
211
|
num_nodes = h.shape[1]
|
207
|
-
a = x.dot(@weight.data) + h.dot(@weight2.data)
|
212
|
+
a = x.dot(@weight.data) + h.dot(@weight2.data)
|
213
|
+
a += @bias.data if @bias
|
208
214
|
|
209
215
|
@forget = @forget_sigmoid.forward(a[true, 0...num_nodes])
|
210
216
|
@g = @g_tanh.forward(a[true, num_nodes...(num_nodes * 2)])
|
@@ -230,7 +236,7 @@ module DNN
|
|
230
236
|
|
231
237
|
@weight.grad += @x.transpose.dot(da)
|
232
238
|
@weight2.grad += @h.transpose.dot(da)
|
233
|
-
@bias.grad += da.sum(0)
|
239
|
+
@bias.grad += da.sum(0) if @bias
|
234
240
|
dx = da.dot(@weight.data.transpose)
|
235
241
|
dh = da.dot(@weight2.data.transpose)
|
236
242
|
dc = dc2_tmp * @forget
|
@@ -247,7 +253,8 @@ module DNN
|
|
247
253
|
weight_initializer: Utils.load_hash(hash[:weight_initializer]),
|
248
254
|
bias_initializer: Utils.load_hash(hash[:bias_initializer]),
|
249
255
|
l1_lambda: hash[:l1_lambda],
|
250
|
-
l2_lambda: hash[:l2_lambda]
|
256
|
+
l2_lambda: hash[:l2_lambda],
|
257
|
+
use_bias: hash[:use_bias])
|
251
258
|
lstm
|
252
259
|
end
|
253
260
|
|
@@ -257,7 +264,8 @@ module DNN
|
|
257
264
|
weight_initializer: RandomNormal.new,
|
258
265
|
bias_initializer: Zeros.new,
|
259
266
|
l1_lambda: 0,
|
260
|
-
l2_lambda: 0
|
267
|
+
l2_lambda: 0,
|
268
|
+
use_bias: true)
|
261
269
|
super
|
262
270
|
@cell = @params[:c] = Param.new
|
263
271
|
end
|
@@ -286,7 +294,7 @@ module DNN
|
|
286
294
|
def backward(dh2s)
|
287
295
|
@weight.grad = Xumo::SFloat.zeros(*@weight.data.shape)
|
288
296
|
@weight2.grad = Xumo::SFloat.zeros(*@weight2.data.shape)
|
289
|
-
@bias.grad = Xumo::SFloat.zeros(*@bias.data.shape)
|
297
|
+
@bias.grad = Xumo::SFloat.zeros(*@bias.data.shape) if @bias
|
290
298
|
unless @return_sequences
|
291
299
|
dh = dh2s
|
292
300
|
dh2s = Xumo::SFloat.zeros(dh.shape[0], @time_length, dh.shape[1])
|
@@ -315,10 +323,10 @@ module DNN
|
|
315
323
|
num_prev_nodes = @input_shape[1]
|
316
324
|
@weight.data = Xumo::SFloat.new(num_prev_nodes, @num_nodes * 4)
|
317
325
|
@weight2.data = Xumo::SFloat.new(@num_nodes, @num_nodes * 4)
|
318
|
-
@bias.data = Xumo::SFloat.new(@num_nodes * 4)
|
326
|
+
@bias.data = Xumo::SFloat.new(@num_nodes * 4) if @bias
|
319
327
|
@weight_initializer.init_param(self, @weight)
|
320
328
|
@weight_initializer.init_param(self, @weight2)
|
321
|
-
@bias_initializer.init_param(self, @bias)
|
329
|
+
@bias_initializer.init_param(self, @bias) if @bias
|
322
330
|
@time_length.times do |t|
|
323
331
|
@layers << LSTM_Dense.new(@weight, @weight2, @bias)
|
324
332
|
end
|
@@ -342,15 +350,19 @@ module DNN
|
|
342
350
|
num_nodes = h.shape[1]
|
343
351
|
@weight_a = @weight.data[true, 0...(num_nodes * 2)]
|
344
352
|
@weight2_a = @weight2.data[true, 0...(num_nodes * 2)]
|
345
|
-
|
346
|
-
a
|
353
|
+
a = x.dot(@weight_a) + h.dot(@weight2_a)
|
354
|
+
a += @bias.data[0...(num_nodes * 2)] if @bias
|
347
355
|
@update = @update_sigmoid.forward(a[true, 0...num_nodes])
|
348
356
|
@reset = @reset_sigmoid.forward(a[true, num_nodes..-1])
|
349
357
|
|
350
358
|
@weight_h = @weight.data[true, (num_nodes * 2)..-1]
|
351
359
|
@weight2_h = @weight2.data[true, (num_nodes * 2)..-1]
|
352
|
-
|
353
|
-
|
360
|
+
@tanh_h = if @bias
|
361
|
+
bias_h = @bias.data[(num_nodes * 2)..-1]
|
362
|
+
@tanh.forward(x.dot(@weight_h) + (h * @reset).dot(@weight2_h) + bias_h)
|
363
|
+
else
|
364
|
+
@tanh.forward(x.dot(@weight_h) + (h * @reset).dot(@weight2_h))
|
365
|
+
end
|
354
366
|
h2 = (1 - @update) * h + @update * @tanh_h
|
355
367
|
h2
|
356
368
|
end
|
@@ -363,7 +375,7 @@ module DNN
|
|
363
375
|
dx = dtanh_h.dot(@weight_h.transpose)
|
364
376
|
dweight2_h = (@h * @reset).transpose.dot(dtanh_h)
|
365
377
|
dh += dtanh_h.dot(@weight2_h.transpose) * @reset
|
366
|
-
dbias_h = dtanh_h.sum(0)
|
378
|
+
dbias_h = dtanh_h.sum(0) if @bias
|
367
379
|
|
368
380
|
dreset = @reset_sigmoid.backward(dtanh_h.dot(@weight2_h.transpose) * @h)
|
369
381
|
dupdate = @update_sigmoid.backward(dh2 * @tanh_h - dh2 * @h)
|
@@ -372,11 +384,11 @@ module DNN
|
|
372
384
|
dx += da.dot(@weight_a.transpose)
|
373
385
|
dweight2_a = @h.transpose.dot(da)
|
374
386
|
dh += da.dot(@weight2_a.transpose)
|
375
|
-
dbias_a = da.sum(0)
|
387
|
+
dbias_a = da.sum(0) if @bias
|
376
388
|
|
377
389
|
@weight.grad += Xumo::SFloat.hstack([dweight_a, dweight_h])
|
378
390
|
@weight2.grad += Xumo::SFloat.hstack([dweight2_a, dweight2_h])
|
379
|
-
@bias.grad += Xumo::SFloat.hstack([dbias_a, dbias_h])
|
391
|
+
@bias.grad += Xumo::SFloat.hstack([dbias_a, dbias_h]) if @bias
|
380
392
|
[dx, dh]
|
381
393
|
end
|
382
394
|
end
|
@@ -390,7 +402,8 @@ module DNN
|
|
390
402
|
weight_initializer: Utils.load_hash(hash[:weight_initializer]),
|
391
403
|
bias_initializer: Utils.load_hash(hash[:bias_initializer]),
|
392
404
|
l1_lambda: hash[:l1_lambda],
|
393
|
-
l2_lambda: hash[:l2_lambda]
|
405
|
+
l2_lambda: hash[:l2_lambda],
|
406
|
+
use_bias: hash[:use_bias])
|
394
407
|
gru
|
395
408
|
end
|
396
409
|
|
@@ -400,7 +413,8 @@ module DNN
|
|
400
413
|
weight_initializer: RandomNormal.new,
|
401
414
|
bias_initializer: Zeros.new,
|
402
415
|
l1_lambda: 0,
|
403
|
-
l2_lambda: 0
|
416
|
+
l2_lambda: 0,
|
417
|
+
use_bias: true)
|
404
418
|
super
|
405
419
|
end
|
406
420
|
|
@@ -411,10 +425,10 @@ module DNN
|
|
411
425
|
num_prev_nodes = @input_shape[1]
|
412
426
|
@weight.data = Xumo::SFloat.new(num_prev_nodes, @num_nodes * 3)
|
413
427
|
@weight2.data = Xumo::SFloat.new(@num_nodes, @num_nodes * 3)
|
414
|
-
@bias.data = Xumo::SFloat.new(@num_nodes * 3)
|
428
|
+
@bias.data = Xumo::SFloat.new(@num_nodes * 3) if @bias
|
415
429
|
@weight_initializer.init_param(self, @weight)
|
416
430
|
@weight_initializer.init_param(self, @weight2)
|
417
|
-
@bias_initializer.init_param(self, @bias)
|
431
|
+
@bias_initializer.init_param(self, @bias) if @bias
|
418
432
|
@time_length.times do |t|
|
419
433
|
@layers << GRU_Dense.new(@weight, @weight2, @bias)
|
420
434
|
end
|
data/lib/dnn/core/utils.rb
CHANGED
@@ -20,14 +20,14 @@ module DNN
|
|
20
20
|
dnn_class.new
|
21
21
|
end
|
22
22
|
|
23
|
-
#
|
24
|
-
# Don't want to write an implementation of the activation function in utils, so we will consider it later.
|
23
|
+
# Return the result of the sigmoid function.
|
25
24
|
def self.sigmoid(x)
|
26
|
-
|
25
|
+
Sigmoid.new.forward(x)
|
27
26
|
end
|
28
27
|
|
28
|
+
# Return the result of the softmax function.
|
29
29
|
def self.softmax(x)
|
30
|
-
|
30
|
+
SoftmaxCrossEntropy.softmax(x)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
end
|
data/lib/dnn/lib/iris.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require "csv"
|
2
|
+
require_relative "downloader"
|
3
|
+
|
4
|
+
module DNN
|
5
|
+
class DNN_Iris_LoadError < DNN_Error; end
|
6
|
+
|
7
|
+
module Iris
|
8
|
+
URL_CSV = "https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data"
|
9
|
+
|
10
|
+
# Iris-setosa
|
11
|
+
SETOSA = 0
|
12
|
+
# Iris-versicolor
|
13
|
+
VERSICOLOR = 1
|
14
|
+
# Iris-virginica
|
15
|
+
VIRGINICA = 2
|
16
|
+
|
17
|
+
def self.downloads
|
18
|
+
return if File.exist?(url_to_file_name(URL_CSV))
|
19
|
+
Downloader.download(URL_CSV)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.load(shuffle = false, shuffle_seed = rand(1 << 31))
|
23
|
+
downloads
|
24
|
+
csv_array = CSV.read(url_to_file_name(URL_CSV)).select { |a| a.length > 0 }
|
25
|
+
x = Numo::SFloat.zeros(csv_array.length, 4)
|
26
|
+
y = Numo::SFloat.zeros(csv_array.length)
|
27
|
+
csv_array.each.with_index do |(sepal_length, sepal_width, petal_length, petal_width, classes), i|
|
28
|
+
x[i, 0] = sepal_length.to_f
|
29
|
+
x[i, 1] = sepal_width.to_f
|
30
|
+
x[i, 2] = petal_length.to_f
|
31
|
+
x[i, 3] = petal_width.to_f
|
32
|
+
y[i] = case classes
|
33
|
+
when "Iris-setosa"
|
34
|
+
SETOSA
|
35
|
+
when "Iris-versicolor"
|
36
|
+
VERSICOLOR
|
37
|
+
when "Iris-virginica"
|
38
|
+
VIRGINICA
|
39
|
+
else
|
40
|
+
raise DNN_Iris_LoadError.new("Unknown class name '#{classes}' for iris")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
if shuffle
|
44
|
+
orig_seed = Random::DEFAULT.seed
|
45
|
+
srand(shuffle_seed)
|
46
|
+
indexs = (0...csv_array.length).to_a.shuffle
|
47
|
+
x[indexs, true] = x
|
48
|
+
y[indexs] = y
|
49
|
+
srand(orig_seed)
|
50
|
+
end
|
51
|
+
[x, y]
|
52
|
+
end
|
53
|
+
|
54
|
+
private_class_method
|
55
|
+
|
56
|
+
def self.url_to_file_name(url)
|
57
|
+
__dir__ + "/" + url.match(%r`.+/(.+)$`)[1]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/dnn/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-dnn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- unagiootoro
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: numo-narray
|
@@ -101,6 +101,7 @@ files:
|
|
101
101
|
- bin/console
|
102
102
|
- bin/setup
|
103
103
|
- examples/cifar10_example.rb
|
104
|
+
- examples/iris_example.rb
|
104
105
|
- examples/mnist_conv2d_example.rb
|
105
106
|
- examples/mnist_example.rb
|
106
107
|
- examples/mnist_lstm_example.rb
|
@@ -126,6 +127,7 @@ files:
|
|
126
127
|
- lib/dnn/lib/cifar10.rb
|
127
128
|
- lib/dnn/lib/downloader.rb
|
128
129
|
- lib/dnn/lib/image.rb
|
130
|
+
- lib/dnn/lib/iris.rb
|
129
131
|
- lib/dnn/lib/mnist.rb
|
130
132
|
- lib/dnn/version.rb
|
131
133
|
- ruby-dnn.gemspec
|