ruby-dnn 0.14.3 → 0.15.0

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