ruby-dnn 1.2.3 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/examples/dcgan/dcgan.rb +1 -1
- data/examples/iris_example.rb +17 -41
- data/examples/iris_example_unused_model.rb +57 -0
- data/examples/vae.rb +1 -1
- data/lib/dnn/core/callbacks.rb +18 -8
- data/lib/dnn/core/iterator.rb +20 -4
- data/lib/dnn/core/layers/rnn_layers.rb +20 -24
- data/lib/dnn/core/models.rb +474 -149
- data/lib/dnn/core/savers.rb +4 -12
- data/lib/dnn/core/utils.rb +14 -0
- data/lib/dnn/datasets/iris.rb +5 -1
- data/lib/dnn/version.rb +1 -1
- data/lib/dnn.rb +32 -26
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df187618941592ff119bab49757f039544d806a6dab5b8c9962040b714a7873e
|
4
|
+
data.tar.gz: 263c5a9d1d366ad4782c0fa30b130a081edae1d6b744daf0b2f359560c83e0fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2b53307f3a90d6fa3caaa01a9f57e7ea3d76a378fa90ecaba4c4e46f052fc1380718a89f066eba9df28a822b49236f10e03c65bb113004b523017412e5cbbf2
|
7
|
+
data.tar.gz: fc6d85c0f8de928f97fad6debf480fa683cad780d70855bedbe8ed989ad31efc92cb60d55a6e456107f76093d461ddfc328c806b51f69095ec08111632438191
|
data/examples/dcgan/dcgan.rb
CHANGED
@@ -121,7 +121,7 @@ class DCGAN < Model
|
|
121
121
|
x
|
122
122
|
end
|
123
123
|
|
124
|
-
def train_step(x_batch, y_batch)
|
124
|
+
def train_step(x_batch, y_batch, need_accuracy: false)
|
125
125
|
batch_size = x_batch.shape[0]
|
126
126
|
noise = Numo::SFloat.new(batch_size, 20).rand(-1, 1)
|
127
127
|
images = @gen.predict(noise)
|
data/examples/iris_example.rb
CHANGED
@@ -3,6 +3,7 @@ require "dnn/datasets/iris"
|
|
3
3
|
# If you use numo/linalg then please uncomment out.
|
4
4
|
# require "numo/linalg/autoloader"
|
5
5
|
|
6
|
+
include DNN::Models
|
6
7
|
include DNN::Layers
|
7
8
|
include DNN::Optimizers
|
8
9
|
include DNN::Losses
|
@@ -14,44 +15,19 @@ x_test, y_test = x[100...150, true], y[100...150]
|
|
14
15
|
y_train = DNN::Utils.to_categorical(y_train, 3, Numo::SFloat)
|
15
16
|
y_test = DNN::Utils.to_categorical(y_test, 3, Numo::SFloat)
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
h = Sigmoid.(h)
|
34
|
-
out = Dot.(h, w2) + b2
|
35
|
-
out
|
36
|
-
end
|
37
|
-
|
38
|
-
(1..epochs).each do |epoch|
|
39
|
-
train_iter.foreach(batch_size) do |x_batch, y_batch, step|
|
40
|
-
x = DNN::Tensor.convert(x_batch)
|
41
|
-
y = DNN::Tensor.convert(y_batch)
|
42
|
-
out = net.(x, y)
|
43
|
-
loss = lf.(out, y)
|
44
|
-
loss.link.backward
|
45
|
-
puts "epoch: #{epoch}, step: #{step}, loss = #{loss.data.to_f}"
|
46
|
-
opt.update([w1, b1, w2, b2])
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
correct = 0
|
51
|
-
test_iter.foreach(batch_size) do |x_batch, y_batch, step|
|
52
|
-
x = DNN::Tensor.convert(x_batch)
|
53
|
-
y = DNN::Tensor.convert(y_batch)
|
54
|
-
out = net.(x, y)
|
55
|
-
correct += out.data.max_index(axis: 1).eq(y_batch.max_index(axis: 1)).count
|
56
|
-
end
|
57
|
-
puts "correct = #{correct}"
|
18
|
+
model = Sequential.new
|
19
|
+
|
20
|
+
model << InputLayer.new(4)
|
21
|
+
|
22
|
+
model << Dense.new(16)
|
23
|
+
model << Sigmoid.new
|
24
|
+
|
25
|
+
model << Dense.new(3)
|
26
|
+
|
27
|
+
model.setup(Adam.new, SoftmaxCrossEntropy.new)
|
28
|
+
|
29
|
+
model.train(x_train, y_train, 1000, batch_size: 32, test: [x_test, y_test])
|
30
|
+
|
31
|
+
accuracy, loss = model.evaluate(x_test, y_test)
|
32
|
+
puts "accuracy: #{accuracy}"
|
33
|
+
puts "loss: #{loss}"
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "dnn"
|
2
|
+
require "dnn/datasets/iris"
|
3
|
+
# If you use numo/linalg then please uncomment out.
|
4
|
+
# require "numo/linalg/autoloader"
|
5
|
+
|
6
|
+
include DNN::Layers
|
7
|
+
include DNN::Optimizers
|
8
|
+
include DNN::Losses
|
9
|
+
|
10
|
+
x, y = DNN::Iris.load(true)
|
11
|
+
x_train, y_train = x[0...100, true], y[0...100]
|
12
|
+
x_test, y_test = x[100...150, true], y[100...150]
|
13
|
+
|
14
|
+
y_train = DNN::Utils.to_categorical(y_train, 3, Numo::SFloat)
|
15
|
+
y_test = DNN::Utils.to_categorical(y_test, 3, Numo::SFloat)
|
16
|
+
|
17
|
+
epochs = 1000
|
18
|
+
batch_size = 32
|
19
|
+
|
20
|
+
opt = Adam.new
|
21
|
+
lf = SoftmaxCrossEntropy.new
|
22
|
+
|
23
|
+
train_iter = DNN::Iterator.new(x_train, y_train)
|
24
|
+
test_iter = DNN::Iterator.new(x_test, y_test, random: false)
|
25
|
+
|
26
|
+
w1 = DNN::Param.new(Numo::SFloat.new(4, 16).rand_norm)
|
27
|
+
b1 = DNN::Param.new(Numo::SFloat.zeros(16))
|
28
|
+
w2 = DNN::Param.new(Numo::SFloat.new(16, 3).rand_norm)
|
29
|
+
b2 = DNN::Param.new(Numo::SFloat.zeros(3))
|
30
|
+
|
31
|
+
net = -> x, y do
|
32
|
+
h = Dot.(x, w1) + b1
|
33
|
+
h = Sigmoid.(h)
|
34
|
+
out = Dot.(h, w2) + b2
|
35
|
+
out
|
36
|
+
end
|
37
|
+
|
38
|
+
(1..epochs).each do |epoch|
|
39
|
+
train_iter.foreach(batch_size) do |x_batch, y_batch, step|
|
40
|
+
x = DNN::Tensor.convert(x_batch)
|
41
|
+
y = DNN::Tensor.convert(y_batch)
|
42
|
+
out = net.(x, y)
|
43
|
+
loss = lf.(out, y)
|
44
|
+
loss.link.backward
|
45
|
+
puts "epoch: #{epoch}, step: #{step}, loss = #{loss.data.to_f}"
|
46
|
+
opt.update([w1, b1, w2, b2])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
correct = 0
|
51
|
+
test_iter.foreach(batch_size) do |x_batch, y_batch, step|
|
52
|
+
x = DNN::Tensor.convert(x_batch)
|
53
|
+
y = DNN::Tensor.convert(y_batch)
|
54
|
+
out = net.(x, y)
|
55
|
+
correct += out.data.max_index(axis: 1).eq(y_batch.max_index(axis: 1)).count
|
56
|
+
end
|
57
|
+
puts "correct = #{correct}"
|
data/examples/vae.rb
CHANGED
data/lib/dnn/core/callbacks.rb
CHANGED
@@ -6,6 +6,12 @@ module DNN
|
|
6
6
|
|
7
7
|
# Please implement the method used for callback event.
|
8
8
|
|
9
|
+
# Process performed before all training.
|
10
|
+
# def before_train; end
|
11
|
+
|
12
|
+
# Process performed after all training.
|
13
|
+
# def after_train; end
|
14
|
+
|
9
15
|
# Process performed before one training.
|
10
16
|
# def before_epoch; end
|
11
17
|
|
@@ -57,7 +63,7 @@ module DNN
|
|
57
63
|
|
58
64
|
# A callback to stop training the model early after test on batch.
|
59
65
|
# @param [Symbol] trigger A log that triggers early stopping.
|
60
|
-
# Specify one of
|
66
|
+
# Specify one of :loss, :test_loss, :test_accuracy
|
61
67
|
# @param [Float] tolerance Tolerance value for early stopping.
|
62
68
|
class EarlyStopping < Callback
|
63
69
|
def initialize(trigger, tolerance)
|
@@ -66,19 +72,21 @@ module DNN
|
|
66
72
|
end
|
67
73
|
|
68
74
|
def after_train_on_batch
|
69
|
-
|
75
|
+
@model.request_early_stop if judge_early_stopping_train
|
70
76
|
end
|
71
77
|
|
72
78
|
def after_epoch
|
73
|
-
|
79
|
+
@model.request_early_stop if judge_early_stopping_test
|
74
80
|
end
|
75
81
|
|
76
82
|
private
|
77
83
|
|
78
84
|
def judge_early_stopping_train
|
79
85
|
case @trigger
|
80
|
-
when :
|
86
|
+
when :loss
|
81
87
|
return true if model.last_log[@trigger] <= @tolerance
|
88
|
+
when :accuracy
|
89
|
+
return true if model.last_log[@trigger] >= @tolerance
|
82
90
|
end
|
83
91
|
false
|
84
92
|
end
|
@@ -97,7 +105,7 @@ module DNN
|
|
97
105
|
# A callback to stop training the model if loss is NaN by after train on batch.
|
98
106
|
class NaNStopping < Callback
|
99
107
|
def after_train_on_batch
|
100
|
-
throw :stop, "loss is NaN." if model.last_log[:
|
108
|
+
throw :stop, "loss is NaN." if model.last_log[:loss].nan?
|
101
109
|
end
|
102
110
|
end
|
103
111
|
|
@@ -105,7 +113,8 @@ module DNN
|
|
105
113
|
# The following logs will be recorded.
|
106
114
|
# epoch: Current epoch.
|
107
115
|
# step: Current step in epoch.
|
108
|
-
#
|
116
|
+
# loss: Batch training loss.
|
117
|
+
# accuracy: Batch training accuracy.
|
109
118
|
# test_loss: Mean test loss.
|
110
119
|
# test_accuracy: Test accuracy.
|
111
120
|
class Logger < Callback
|
@@ -113,7 +122,8 @@ module DNN
|
|
113
122
|
@log = {
|
114
123
|
epoch: [],
|
115
124
|
step: [],
|
116
|
-
|
125
|
+
loss: [],
|
126
|
+
accuracy: [],
|
117
127
|
test_loss: [],
|
118
128
|
test_accuracy: [],
|
119
129
|
}
|
@@ -124,7 +134,7 @@ module DNN
|
|
124
134
|
end
|
125
135
|
|
126
136
|
def after_train_on_batch
|
127
|
-
logging(:
|
137
|
+
logging(:loss, :step)
|
128
138
|
end
|
129
139
|
|
130
140
|
# Get a log.
|
data/lib/dnn/core/iterator.rb
CHANGED
@@ -4,11 +4,13 @@ module DNN
|
|
4
4
|
attr_reader :num_datas
|
5
5
|
attr_reader :last_round_down
|
6
6
|
|
7
|
-
# @param [Numo::
|
8
|
-
# @param [Numo::
|
7
|
+
# @param [Numo::NArray | Array] x_datas input datas.
|
8
|
+
# @param [Numo::NArray | Array] y_datas output datas.
|
9
9
|
# @param [Boolean] random Set true to return batches randomly. Setting false returns batches in order of index.
|
10
10
|
# @param [Boolean] last_round_down Set true to round down for last batch data when call foreach.
|
11
11
|
def initialize(x_datas, y_datas, random: true, last_round_down: false)
|
12
|
+
Utils.check_input_data_type("x_datas", x_datas, Xumo::NArray)
|
13
|
+
Utils.check_input_data_type("y_datas", y_datas, Xumo::NArray)
|
12
14
|
@x_datas = x_datas
|
13
15
|
@y_datas = y_datas
|
14
16
|
@random = random
|
@@ -64,12 +66,26 @@ module DNN
|
|
64
66
|
# @param [Integer] batch_size Batch size.
|
65
67
|
# @yield Executes block by receiving the specified arguments (x_batch, y_batch).
|
66
68
|
def foreach(batch_size, &block)
|
67
|
-
|
68
|
-
steps.times do |step|
|
69
|
+
max_steps(batch_size).times do |step|
|
69
70
|
x_batch, y_batch = next_batch(batch_size)
|
70
71
|
block.call(x_batch, y_batch, step)
|
71
72
|
end
|
72
73
|
reset
|
73
74
|
end
|
75
|
+
|
76
|
+
# Return the number of available data considering last_round_down.
|
77
|
+
def num_usable_datas(batch_size)
|
78
|
+
if @last_round_down
|
79
|
+
max_steps(batch_size) * batch_size
|
80
|
+
else
|
81
|
+
@num_datas
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Get max steps for iteration.
|
86
|
+
# @param [Integer] batch_size Batch size.
|
87
|
+
def max_steps(batch_size)
|
88
|
+
@last_round_down ? @num_datas / batch_size : (@num_datas.to_f / batch_size).ceil
|
89
|
+
end
|
74
90
|
end
|
75
91
|
end
|
@@ -1,5 +1,16 @@
|
|
1
1
|
module DNN
|
2
2
|
module Layers
|
3
|
+
# Super class of all RNN cells.
|
4
|
+
class RNNCell
|
5
|
+
attr_accessor :trainable
|
6
|
+
|
7
|
+
def initialize(weight, recurrent_weight, bias)
|
8
|
+
@weight = weight
|
9
|
+
@recurrent_weight = recurrent_weight
|
10
|
+
@bias = bias
|
11
|
+
@trainable = true
|
12
|
+
end
|
13
|
+
end
|
3
14
|
|
4
15
|
# Super class of all RNN classes.
|
5
16
|
class RNN < Connection
|
@@ -136,15 +147,10 @@ module DNN
|
|
136
147
|
end
|
137
148
|
end
|
138
149
|
|
139
|
-
class
|
140
|
-
attr_accessor :trainable
|
141
|
-
|
150
|
+
class SimpleRNNCell < RNNCell
|
142
151
|
def initialize(weight, recurrent_weight, bias, activation)
|
143
|
-
|
144
|
-
@recurrent_weight = recurrent_weight
|
145
|
-
@bias = bias
|
152
|
+
super(weight, recurrent_weight, bias)
|
146
153
|
@activation = activation.clone
|
147
|
-
@trainable = true
|
148
154
|
end
|
149
155
|
|
150
156
|
def forward(x, h)
|
@@ -206,7 +212,7 @@ module DNN
|
|
206
212
|
end
|
207
213
|
|
208
214
|
def create_hidden_layer
|
209
|
-
@hidden_layers = Array.new(@time_length) {
|
215
|
+
@hidden_layers = Array.new(@time_length) { SimpleRNNCell.new(@weight, @recurrent_weight, @bias, @activation) }
|
210
216
|
end
|
211
217
|
|
212
218
|
def to_hash
|
@@ -228,19 +234,14 @@ module DNN
|
|
228
234
|
end
|
229
235
|
end
|
230
236
|
|
231
|
-
class
|
232
|
-
attr_accessor :trainable
|
233
|
-
|
237
|
+
class LSTMCell < RNNCell
|
234
238
|
def initialize(weight, recurrent_weight, bias)
|
235
|
-
|
236
|
-
@recurrent_weight = recurrent_weight
|
237
|
-
@bias = bias
|
239
|
+
super(weight, recurrent_weight, bias)
|
238
240
|
@tanh = Layers::Tanh.new
|
239
241
|
@g_tanh = Layers::Tanh.new
|
240
242
|
@forget_sigmoid = Layers::Sigmoid.new
|
241
243
|
@in_sigmoid = Layers::Sigmoid.new
|
242
244
|
@out_sigmoid = Layers::Sigmoid.new
|
243
|
-
@trainable = true
|
244
245
|
end
|
245
246
|
|
246
247
|
def forward(x, h, c)
|
@@ -312,7 +313,7 @@ module DNN
|
|
312
313
|
end
|
313
314
|
|
314
315
|
def create_hidden_layer
|
315
|
-
@hidden_layers = Array.new(@time_length) {
|
316
|
+
@hidden_layers = Array.new(@time_length) { LSTMCell.new(@weight, @recurrent_weight, @bias) }
|
316
317
|
end
|
317
318
|
|
318
319
|
def forward_node(xs)
|
@@ -365,17 +366,12 @@ module DNN
|
|
365
366
|
end
|
366
367
|
end
|
367
368
|
|
368
|
-
class
|
369
|
-
attr_accessor :trainable
|
370
|
-
|
369
|
+
class GRUCell < RNNCell
|
371
370
|
def initialize(weight, recurrent_weight, bias)
|
372
|
-
|
373
|
-
@recurrent_weight = recurrent_weight
|
374
|
-
@bias = bias
|
371
|
+
super(weight, recurrent_weight, bias)
|
375
372
|
@update_sigmoid = Layers::Sigmoid.new
|
376
373
|
@reset_sigmoid = Layers::Sigmoid.new
|
377
374
|
@tanh = Layers::Tanh.new
|
378
|
-
@trainable = true
|
379
375
|
end
|
380
376
|
|
381
377
|
def forward(x, h)
|
@@ -457,7 +453,7 @@ module DNN
|
|
457
453
|
end
|
458
454
|
|
459
455
|
def create_hidden_layer
|
460
|
-
@hidden_layers = Array.new(@time_length) {
|
456
|
+
@hidden_layers = Array.new(@time_length) { GRUCell.new(@weight, @recurrent_weight, @bias) }
|
461
457
|
end
|
462
458
|
end
|
463
459
|
|