ruby-dnn 0.14.3 → 0.15.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e4633ce695dc370e62c6653d5ff07d81f9306625f7165750f8e455c4a28ca61
4
- data.tar.gz: e4275720191b14592e2fb93581e9826e437ce9fcc75f6473cb963892d8f9f893
3
+ metadata.gz: 6be46c1c89fbb1f1f2091c95f15ccab21473d52571ee47953a4c07446ea8544c
4
+ data.tar.gz: e01eb929c13b2a33a350ee42b8bb8584446722ac3ba3dc7e163f7f3429ec068a
5
5
  SHA512:
6
- metadata.gz: d9831b1b3a73423742bce7f989a13c7ac3d63426a2bd8366143be1717a1e90fbdf6aa79a9e90508e193e00fbcd120942b7ccd0c8b6b13eb9cc4e237471712030
7
- data.tar.gz: 60344f9eb3645b0560dd9d1f63a86dcc554f7c9b923a3b84a0bc301264120d50ca034d2887c1cc41989f223891ace4d64c570c9935e2b3f6f467a05f0fab910f
6
+ metadata.gz: cc5bd6608d17a90cb97f6f5530877e5441a2dc73fc18e7b821c33d606469d6a15c22d519570b8c8821282915de84b677b3527221b1eb076a145a85997a58d2d9
7
+ data.tar.gz: 721c578f41a7648dfc5c462a1b9a81f6b9604b50540e8fa4a58bb801adc8059ee7cff9f27d3c1b1a710ce0857e426ca2087f4902bf46d9c621870e3ae7cb664a
data/README.md CHANGED
@@ -2,8 +2,9 @@
2
2
  [![Gem Version](https://badge.fury.io/rb/ruby-dnn.svg)](https://badge.fury.io/rb/ruby-dnn)
3
3
  [![Build Status](https://travis-ci.org/unagiootoro/ruby-dnn.svg?branch=master)](https://travis-ci.org/unagiootoro/ruby-dnn)
4
4
 
5
- ruby-dnn is a ruby deep learning library. This library supports full connected neural network and convolution neural network.
6
- Currently, you can get 99% accuracy with MNIST and 74% with CIFAR 10.
5
+ ruby-dnn is a ruby deep learning library. This library supports full connected neural network and convolution neural network
6
+ and recurrent neural network.
7
+ Currently, you can get 99% accuracy with MNIST and 78% with CIFAR 10.
7
8
 
8
9
  ## Installation
9
10
 
@@ -79,8 +80,9 @@ If you want to know more detailed information, please refer to the source code.
79
80
  || Implemented classes |
80
81
  |:-----------|------------:|
81
82
  | Connections | Dense, Conv2D, Conv2DTranspose, Embedding, SimpleRNN, LSTM, GRU |
82
- | Layers | Flatten, Reshape, Dropout, BatchNormalization, MaxPool2D, AvgPool2D, UnPool2D |
83
83
  | Activations | Sigmoid, Tanh, Softsign, Softplus, Swish, ReLU, LeakyReLU, ELU |
84
+ | Basic | Flatten, Reshape, Dropout, BatchNormalization |
85
+ | Pooling | MaxPool2D, AvgPool2D, GlobalAvgPool2D, UnPool2D |
84
86
  | Optimizers | SGD, Nesterov, AdaGrad, RMSProp, AdaDelta, RMSPropGraves, Adam, AdaBound |
85
87
  | Losses | MeanSquaredError, MeanAbsoluteError, Hinge, HuberLoss, SoftmaxCrossEntropy, SigmoidCrossEntropy |
86
88
 
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ Rake::TestTask.new(:test) do |t|
5
5
  t.libs << "test"
6
6
  t.libs << "ext"
7
7
  t.libs << "lib"
8
- t.test_files = FileList["test/*_test.rb"]
8
+ t.test_files = FileList["test/*_test.rb", "test/layers_test/*_test.rb"]
9
9
  end
10
10
 
11
11
  task :build_cifar_loader do
@@ -27,7 +27,9 @@ end
27
27
  task :default => [:test, :build_cifar_loader, :build_rb_stb_image]
28
28
 
29
29
  task :doc do
30
- src_list = Dir["lib/dnn/core/*.rb"]
30
+ src_list = Dir["lib/dnn.rb"]
31
+ src_list += Dir["lib/dnn/core/*.rb"]
32
+ src_list += Dir["lib/dnn/core/layers/*.rb"]
31
33
  src_list += Dir["lib/dnn/*.rb"]
32
34
  sh "yardoc #{src_list.join(' ')}"
33
35
  end
@@ -20,9 +20,9 @@ BATCH_SIZE = 128
20
20
  # Select save style from USE_MARSHAL or USE_JSON.
21
21
  SAVE_STYLE = USE_MARSHAL
22
22
 
23
- # When set a true, save data included optimizer status.
23
+ # When set a true, save data included model structure.
24
24
  # This setting is enabled when SAVE_STYLE is USE_MARSHAL.
25
- INCLUDE_OPTIMIZER = false
25
+ INCLUDE_MODEL = true
26
26
 
27
27
  x_train, y_train = MNIST.load_train
28
28
  x_test, y_test = MNIST.load_test
@@ -47,7 +47,7 @@ class MLP < Model
47
47
  end
48
48
 
49
49
  def call(x)
50
- x = InputLayer.(x)
50
+ x = InputLayer.new(784).(x)
51
51
  x = @l1.(x)
52
52
  x = @bn1.(x)
53
53
  x = ReLU.(x)
@@ -64,7 +64,7 @@ model.setup(Adam.new, SoftmaxCrossEntropy.new)
64
64
  model.train(x_train, y_train, EPOCHS, batch_size: BATCH_SIZE, test: [x_test, y_test])
65
65
 
66
66
  if SAVE_STYLE == USE_MARSHAL
67
- saver = MarshalSaver.new(model, include_optimizer: INCLUDE_OPTIMIZER)
67
+ saver = MarshalSaver.new(model, include_model: INCLUDE_MODEL)
68
68
  saver.save("trained_mnist.marshal")
69
69
  # model.save("trained_mnist.marshal") # This code is equivalent to the code above.
70
70
  elsif SAVE_STYLE == USE_JSON
@@ -73,6 +73,8 @@ elsif SAVE_STYLE == USE_JSON
73
73
  end
74
74
 
75
75
  model2 = MLP.new
76
+ model2.setup(Adam.new, SoftmaxCrossEntropy.new)
77
+ model2.predict1(Numo::SFloat.zeros(784))
76
78
  if SAVE_STYLE == USE_MARSHAL
77
79
  loader = MarshalLoader.new(model2)
78
80
  loader.load("trained_mnist.marshal")
@@ -82,4 +84,4 @@ elsif SAVE_STYLE == USE_JSON
82
84
  loader.load("trained_mnist.json")
83
85
  end
84
86
 
85
- puts model2.accuracy(x_test, y_test)
87
+ puts model2.evaluate(x_test, y_test)
@@ -8,13 +8,8 @@ Image = DNN::Image
8
8
 
9
9
  batch_size = 100
10
10
 
11
- gen = Generator.new
12
- dis = Discriminator.new
13
- dcgan = DCGAN.new(gen, dis)
14
- dcgan.predict1(Numo::SFloat.zeros(20))
15
-
16
- loader = MarshalLoader.new(dcgan)
17
- loader.load("trained/dcgan_model_epoch20.marshal")
11
+ dcgan = DCGAN.load("trained/dcgan_model_epoch20.marshal")
12
+ gen = dcgan.gen
18
13
 
19
14
  Numo::SFloat.srand(rand(1 << 31))
20
15
  noise = Numo::SFloat.new(batch_size, 20).rand(-1, 1)
@@ -20,7 +20,6 @@ dcgan = DCGAN.new(gen, dis)
20
20
  dis.setup(Adam.new(alpha: 0.00001, beta1: 0.1), SigmoidCrossEntropy.new)
21
21
  dcgan.setup(Adam.new(alpha: 0.0002, beta1: 0.5), SigmoidCrossEntropy.new)
22
22
  dcgan.add_callback(CheckPoint.new("trained/dcgan_model"))
23
- dcgan.predict1(Numo::SFloat.zeros(20))
24
23
 
25
24
  x_train, * = MNIST.load_train
26
25
  x_train = Numo::SFloat.cast(x_train)
data/lib/dnn.rb CHANGED
@@ -11,21 +11,21 @@ require_relative "dnn/version"
11
11
  require_relative "dnn/core/error"
12
12
  require_relative "dnn/core/global"
13
13
  require_relative "dnn/core/tensor"
14
- require_relative "dnn/core/models"
15
14
  require_relative "dnn/core/param"
16
15
  require_relative "dnn/core/link"
17
16
  require_relative "dnn/core/iterator"
18
- require_relative "dnn/core/initializers"
19
- require_relative "dnn/core/layers"
20
- require_relative "dnn/core/normalizations"
21
- require_relative "dnn/core/activations"
22
- require_relative "dnn/core/merge_layers"
17
+ require_relative "dnn/core/models"
18
+ require_relative "dnn/core/layers/basic_layers"
19
+ require_relative "dnn/core/layers/normalizations"
20
+ require_relative "dnn/core/layers/activations"
21
+ require_relative "dnn/core/layers/merge_layers"
22
+ require_relative "dnn/core/layers/cnn_layers"
23
+ require_relative "dnn/core/layers/embedding"
24
+ require_relative "dnn/core/layers/rnn_layers"
25
+ require_relative "dnn/core/optimizers"
23
26
  require_relative "dnn/core/losses"
27
+ require_relative "dnn/core/initializers"
24
28
  require_relative "dnn/core/regularizers"
25
- require_relative "dnn/core/cnn_layers"
26
- require_relative "dnn/core/embedding"
27
- require_relative "dnn/core/rnn_layers"
28
- require_relative "dnn/core/optimizers"
29
29
  require_relative "dnn/core/callbacks"
30
30
  require_relative "dnn/core/savers"
31
31
  require_relative "dnn/core/utils"
@@ -35,13 +35,17 @@ module DNN
35
35
  end
36
36
 
37
37
  # A callback that save the model at the after of the epoch.
38
+ # @param [String] base_file_name Base file name for saving.
39
+ # @param [Boolean] include_model When set a true, save data included model structure.
38
40
  class CheckPoint < Callback
39
- def initialize(base_file_name)
41
+ def initialize(base_file_name, include_model: true)
40
42
  @base_file_name = base_file_name
43
+ @include_model = include_model
41
44
  end
42
45
 
43
46
  def after_epoch
44
- model.save(@base_file_name + "_epoch#{model.last_log[:epoch]}.marshal")
47
+ saver = Savers::MarshalSaver.new(@model, include_model: @include_model)
48
+ saver.save(@base_file_name + "_epoch#{model.last_log[:epoch]}.marshal")
45
49
  end
46
50
  end
47
51
 
@@ -4,8 +4,8 @@ module DNN
4
4
  attr_reader :num_datas
5
5
  attr_reader :last_round_down
6
6
 
7
- # @param [Numo::SFloat] x_datas input datas.
8
- # @param [Numo::SFloat] y_datas output datas.
7
+ # @param [Numo::SFloat | Array] x_datas input datas.
8
+ # @param [Numo::SFloat | 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)
@@ -19,6 +19,7 @@ module DNN
19
19
 
20
20
  # Return the next batch.
21
21
  # @param [Integer] batch_size Required batch size.
22
+ # @return [Array] Returns the mini batch in the form [x_batch, y_batch].
22
23
  def next_batch(batch_size)
23
24
  raise DNN_Error, "This iterator has not next batch. Please call reset." unless has_next?
24
25
  if @indexes.length <= batch_size
@@ -27,6 +28,13 @@ module DNN
27
28
  else
28
29
  batch_indexes = @indexes.shift(batch_size)
29
30
  end
31
+ get_batch(batch_indexes)
32
+ end
33
+
34
+ # Implement a process to get mini batch.
35
+ # @param [Array] batch_indexes Index of batch to get.
36
+ # @return [Array] Returns the mini batch in the form [x_batch, y_batch].
37
+ private def get_batch(batch_indexes)
30
38
  x_batch = if @x_datas.is_a?(Array)
31
39
  @x_datas.map { |datas| datas[batch_indexes, false] }
32
40
  else
@@ -3,7 +3,6 @@ module DNN
3
3
 
4
4
  # Super class of all layer classes.
5
5
  class Layer
6
- attr_accessor :name
7
6
  attr_reader :input_shape
8
7
 
9
8
  def self.call(x, *args)
@@ -16,13 +15,11 @@ module DNN
16
15
  layer = layer_class.allocate
17
16
  raise DNN_Error, "#{layer.class} is not an instance of #{self} class." unless layer.is_a?(self)
18
17
  layer.load_hash(hash)
19
- layer.name = hash[:name]&.to_sym
20
18
  layer
21
19
  end
22
20
 
23
21
  def initialize
24
22
  @built = false
25
- @name = nil
26
23
  end
27
24
 
28
25
  # Forward propagation and create a link.
@@ -70,7 +67,7 @@ module DNN
70
67
 
71
68
  # Layer to a hash.
72
69
  def to_hash(merge_hash = nil)
73
- hash = { class: self.class.name, name: @name }
70
+ hash = { class: self.class.name }
74
71
  hash.merge!(merge_hash) if merge_hash
75
72
  hash
76
73
  end
@@ -78,10 +75,20 @@ module DNN
78
75
  def load_hash(hash)
79
76
  initialize
80
77
  end
78
+
79
+ def clean
80
+ input_shape = @input_shape
81
+ hash = to_hash
82
+ instance_variables.each do |ivar|
83
+ instance_variable_set(ivar, nil)
84
+ end
85
+ load_hash(hash)
86
+ build(input_shape)
87
+ end
81
88
  end
82
89
 
83
90
  # This class is a superclass of all classes with learning parameters.
84
- class HasParamLayer < Layer
91
+ class TrainableLayer < Layer
85
92
  # @return [Boolean] Setting false prevents learning of parameters.
86
93
  attr_accessor :trainable
87
94
 
@@ -94,6 +101,22 @@ module DNN
94
101
  def get_params
95
102
  raise NotImplementedError, "Class '#{self.class.name}' has implement method 'get_params'"
96
103
  end
104
+
105
+ def clean
106
+ input_shape = @input_shape
107
+ hash = to_hash
108
+ params = get_params
109
+ instance_variables.each do |ivar|
110
+ instance_variable_set(ivar, nil)
111
+ end
112
+ load_hash(hash)
113
+ build(input_shape)
114
+ params.each do |(key, param)|
115
+ param.data = nil
116
+ param.grad = Xumo::SFloat[0] if param.grad
117
+ instance_variable_set("@#{key}", param)
118
+ end
119
+ end
97
120
  end
98
121
 
99
122
  class InputLayer < Layer
@@ -109,7 +132,7 @@ module DNN
109
132
  end
110
133
 
111
134
  def call(input)
112
- build unless built?
135
+ build(@input_shape) unless built?
113
136
  if input.is_a?(Tensor)
114
137
  x = input.data
115
138
  prev_link = input&.link
@@ -120,7 +143,7 @@ module DNN
120
143
  Tensor.new(forward(x), Link.new(prev_link, self))
121
144
  end
122
145
 
123
- def build
146
+ def build(input_shape)
124
147
  @built = true
125
148
  end
126
149
 
@@ -163,7 +186,7 @@ module DNN
163
186
  end
164
187
 
165
188
  # It is a superclass of all connection layers.
166
- class Connection < HasParamLayer
189
+ class Connection < TrainableLayer
167
190
  attr_reader :weight
168
191
  attr_reader :bias
169
192
  attr_reader :weight_initializer
@@ -1,7 +1,7 @@
1
1
  module DNN
2
2
  module Layers
3
3
 
4
- class Embedding < HasParamLayer
4
+ class Embedding < TrainableLayer
5
5
  attr_reader :input_length
6
6
  attr_reader :weight
7
7
  attr_reader :weight_initializer
@@ -19,16 +19,17 @@ module DNN
19
19
  @input_length = input_length
20
20
  @weight_initializer = weight_initializer
21
21
  @weight_regularizer = weight_regularizer
22
+ @weight = Param.new(nil, Xumo::SFloat[0])
22
23
  end
23
24
 
24
25
  def call(input_tensor)
25
- build unless built?
26
+ build(@input_shape) unless built?
26
27
  Tensor.new(forward(input_tensor.data), Link.new(nil, self))
27
28
  end
28
29
 
29
- def build
30
+ def build(input_shape)
30
31
  @built = true
31
- @weight = Param.new(Xumo::SFloat.new(@input_length), Xumo::SFloat[0])
32
+ @weight.data = Xumo::SFloat.new(@input_length)
32
33
  @weight_initializer.init_param(self, @weight)
33
34
  @weight_regularizer.param = @weight if @weight_regularizer
34
35
  end
@@ -1,5 +1,5 @@
1
1
  module DNN
2
- module MergeLayers
2
+ module Layers
3
3
 
4
4
  class MergeLayer < Layers::Layer
5
5
  def self.call(x1, x2, *args)
@@ -1,7 +1,7 @@
1
1
  module DNN
2
2
  module Layers
3
3
 
4
- class BatchNormalization < HasParamLayer
4
+ class BatchNormalization < TrainableLayer
5
5
  attr_reader :gamma
6
6
  attr_reader :beta
7
7
  attr_reader :running_mean
@@ -18,14 +18,18 @@ module DNN
18
18
  @axis = axis
19
19
  @momentum = momentum
20
20
  @eps = eps
21
+ @gamma = Param.new(nil, Xumo::SFloat[0])
22
+ @beta = Param.new(nil, Xumo::SFloat[0])
23
+ @running_mean = Param.new
24
+ @running_var = Param.new
21
25
  end
22
26
 
23
27
  def build(input_shape)
24
28
  super
25
- @gamma = Param.new(Xumo::SFloat.ones(*output_shape), Xumo::SFloat[0])
26
- @beta = Param.new(Xumo::SFloat.zeros(*output_shape), Xumo::SFloat[0])
27
- @running_mean = Param.new(Xumo::SFloat.zeros(*output_shape))
28
- @running_var = Param.new(Xumo::SFloat.zeros(*output_shape))
29
+ @gamma.data = Xumo::SFloat.ones(*output_shape)
30
+ @beta.data = Xumo::SFloat.zeros(*output_shape)
31
+ @running_mean.data = Xumo::SFloat.zeros(*output_shape)
32
+ @running_var.data = Xumo::SFloat.zeros(*output_shape)
29
33
  end
30
34
 
31
35
  def forward(x)
@@ -31,7 +31,7 @@ module DNN
31
31
  @num_nodes = num_nodes
32
32
  @stateful = stateful
33
33
  @return_sequences = return_sequences
34
- @layers = []
34
+ @hidden_layers = []
35
35
  @hidden = Param.new
36
36
  @recurrent_weight = Param.new(nil, Xumo::SFloat[0])
37
37
  @recurrent_weight_initializer = recurrent_weight_initializer
@@ -46,14 +46,19 @@ module DNN
46
46
  @time_length = @input_shape[0]
47
47
  end
48
48
 
49
+ private def create_hidden_layer
50
+ raise NotImplementedError, "Class '#{self.class.name}' has implement method 'create_hidden_layer'"
51
+ end
52
+
49
53
  def forward(xs)
54
+ create_hidden_layer
50
55
  @xs_shape = xs.shape
51
56
  hs = Xumo::SFloat.zeros(xs.shape[0], @time_length, @num_nodes)
52
57
  h = @stateful && @hidden.data ? @hidden.data : Xumo::SFloat.zeros(xs.shape[0], @num_nodes)
53
58
  xs.shape[1].times do |t|
54
59
  x = xs[true, t, false]
55
- @layers[t].trainable = @trainable
56
- h = @layers[t].forward(x, h)
60
+ @hidden_layers[t].trainable = @trainable
61
+ h = @hidden_layers[t].forward(x, h)
57
62
  hs[true, t, false] = h
58
63
  end
59
64
  @hidden.data = h
@@ -70,7 +75,7 @@ module DNN
70
75
  dh = 0
71
76
  (dh2s.shape[1] - 1).downto(0) do |t|
72
77
  dh2 = dh2s[true, t, false]
73
- dx, dh = @layers[t].backward(dh2 + dh)
78
+ dx, dh = @hidden_layers[t].backward(dh2 + dh)
74
79
  dxs[true, t, false] = dx
75
80
  end
76
81
  dxs
@@ -196,9 +201,10 @@ module DNN
196
201
  @recurrent_weight.data = Xumo::SFloat.new(@num_nodes, @num_nodes)
197
202
  @bias.data = Xumo::SFloat.new(@num_nodes) if @bias
198
203
  init_weight_and_bias
199
- @time_length.times do
200
- @layers << SimpleRNNDense.new(@weight, @recurrent_weight, @bias, @activation)
201
- end
204
+ end
205
+
206
+ def create_hidden_layer
207
+ @hidden_layers = Array.new(@time_length) { SimpleRNNDense.new(@weight, @recurrent_weight, @bias, @activation) }
202
208
  end
203
209
 
204
210
  def to_hash
@@ -301,12 +307,14 @@ module DNN
301
307
  @recurrent_weight.data = Xumo::SFloat.new(@num_nodes, @num_nodes * 4)
302
308
  @bias.data = Xumo::SFloat.new(@num_nodes * 4) if @bias
303
309
  init_weight_and_bias
304
- @time_length.times do
305
- @layers << LSTMDense.new(@weight, @recurrent_weight, @bias)
306
- end
310
+ end
311
+
312
+ def create_hidden_layer
313
+ @hidden_layers = Array.new(@time_length) { LSTMDense.new(@weight, @recurrent_weight, @bias) }
307
314
  end
308
315
 
309
316
  def forward(xs)
317
+ create_hidden_layer
310
318
  @xs_shape = xs.shape
311
319
  hs = Xumo::SFloat.zeros(xs.shape[0], @time_length, @num_nodes)
312
320
  h = nil
@@ -319,8 +327,8 @@ module DNN
319
327
  c ||= Xumo::SFloat.zeros(xs.shape[0], @num_nodes)
320
328
  xs.shape[1].times do |t|
321
329
  x = xs[true, t, false]
322
- @layers[t].trainable = @trainable
323
- h, c = @layers[t].forward(x, h, c)
330
+ @hidden_layers[t].trainable = @trainable
331
+ h, c = @hidden_layers[t].forward(x, h, c)
324
332
  hs[true, t, false] = h
325
333
  end
326
334
  @hidden.data = h
@@ -339,7 +347,7 @@ module DNN
339
347
  dc = 0
340
348
  (dh2s.shape[1] - 1).downto(0) do |t|
341
349
  dh2 = dh2s[true, t, false]
342
- dx, dh, dc = @layers[t].backward(dh2 + dh, dc)
350
+ dx, dh, dc = @hidden_layers[t].backward(dh2 + dh, dc)
343
351
  dxs[true, t, false] = dx
344
352
  end
345
353
  dxs
@@ -444,9 +452,10 @@ module DNN
444
452
  @recurrent_weight.data = Xumo::SFloat.new(@num_nodes, @num_nodes * 3)
445
453
  @bias.data = Xumo::SFloat.new(@num_nodes * 3) if @bias
446
454
  init_weight_and_bias
447
- @time_length.times do
448
- @layers << GRUDense.new(@weight, @recurrent_weight, @bias)
449
- end
455
+ end
456
+
457
+ def create_hidden_layer
458
+ @hidden_layers = Array.new(@time_length) { GRUDense.new(@weight, @recurrent_weight, @bias) }
450
459
  end
451
460
  end
452
461