ruby-dnn 0.9.3 → 0.9.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|