ruby-dnn 0.10.1 → 0.10.2
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/README.md +2 -2
- data/examples/cifar100_example.rb +71 -71
- data/examples/cifar10_example.rb +68 -68
- data/examples/iris_example.rb +34 -34
- data/examples/mnist_conv2d_example.rb +50 -50
- data/examples/mnist_example.rb +39 -39
- data/examples/mnist_lstm_example.rb +36 -36
- data/examples/xor_example.rb +24 -24
- data/lib/dnn.rb +27 -26
- data/lib/dnn/cifar10.rb +51 -51
- data/lib/dnn/cifar100.rb +49 -49
- data/lib/dnn/core/activations.rb +148 -148
- data/lib/dnn/core/cnn_layers.rb +464 -464
- data/lib/dnn/core/dataset.rb +34 -34
- data/lib/dnn/core/embedding.rb +56 -0
- data/lib/dnn/core/error.rb +5 -5
- data/lib/dnn/core/initializers.rb +126 -126
- data/lib/dnn/core/layers.rb +307 -307
- data/lib/dnn/core/losses.rb +175 -175
- data/lib/dnn/core/model.rb +461 -461
- data/lib/dnn/core/normalizations.rb +72 -72
- data/lib/dnn/core/optimizers.rb +283 -283
- data/lib/dnn/core/param.rb +9 -9
- data/lib/dnn/core/regularizers.rb +106 -106
- data/lib/dnn/core/rnn_layers.rb +464 -464
- data/lib/dnn/core/utils.rb +34 -34
- data/lib/dnn/downloader.rb +50 -50
- data/lib/dnn/image.rb +41 -41
- data/lib/dnn/iris.rb +60 -60
- data/lib/dnn/mnist.rb +84 -84
- data/lib/dnn/version.rb +3 -3
- metadata +2 -1
data/lib/dnn/core/dataset.rb
CHANGED
@@ -1,34 +1,34 @@
|
|
1
|
-
# This class manages input datas and output datas together.
|
2
|
-
class DNN::Dataset
|
3
|
-
# @param [Numo::SFloat] x_datas input datas.
|
4
|
-
# @param [Numo::SFloat] y_datas output datas.
|
5
|
-
# @param [Bool] random Set true to return batches randomly. Setting false returns batches in order of index.
|
6
|
-
def initialize(x_datas, y_datas, random = true)
|
7
|
-
@x_datas = x_datas
|
8
|
-
@y_datas = y_datas
|
9
|
-
@random = random
|
10
|
-
@num_datas = x_datas.shape[0]
|
11
|
-
reset_indexs
|
12
|
-
end
|
13
|
-
|
14
|
-
# Return the next batch.
|
15
|
-
# If the number of remaining data < batch size, if random = true, shuffle the data again and return a batch.
|
16
|
-
# If random = false, all remaining data will be returned regardless of the batch size.
|
17
|
-
def next_batch(batch_size)
|
18
|
-
if @indexes.length < batch_size
|
19
|
-
batch_indexes = @indexes unless @random
|
20
|
-
reset_indexs
|
21
|
-
batch_indexes = @indexes.shift(batch_size) if @random
|
22
|
-
else
|
23
|
-
batch_indexes = @indexes.shift(batch_size)
|
24
|
-
end
|
25
|
-
x_batch = @x_datas[batch_indexes, false]
|
26
|
-
y_batch = @y_datas[batch_indexes, false]
|
27
|
-
[x_batch, y_batch]
|
28
|
-
end
|
29
|
-
|
30
|
-
private def reset_indexs
|
31
|
-
@indexes = @num_datas.times.to_a
|
32
|
-
@indexes.shuffle! if @random
|
33
|
-
end
|
34
|
-
end
|
1
|
+
# This class manages input datas and output datas together.
|
2
|
+
class DNN::Dataset
|
3
|
+
# @param [Numo::SFloat] x_datas input datas.
|
4
|
+
# @param [Numo::SFloat] y_datas output datas.
|
5
|
+
# @param [Bool] random Set true to return batches randomly. Setting false returns batches in order of index.
|
6
|
+
def initialize(x_datas, y_datas, random = true)
|
7
|
+
@x_datas = x_datas
|
8
|
+
@y_datas = y_datas
|
9
|
+
@random = random
|
10
|
+
@num_datas = x_datas.shape[0]
|
11
|
+
reset_indexs
|
12
|
+
end
|
13
|
+
|
14
|
+
# Return the next batch.
|
15
|
+
# If the number of remaining data < batch size, if random = true, shuffle the data again and return a batch.
|
16
|
+
# If random = false, all remaining data will be returned regardless of the batch size.
|
17
|
+
def next_batch(batch_size)
|
18
|
+
if @indexes.length < batch_size
|
19
|
+
batch_indexes = @indexes unless @random
|
20
|
+
reset_indexs
|
21
|
+
batch_indexes = @indexes.shift(batch_size) if @random
|
22
|
+
else
|
23
|
+
batch_indexes = @indexes.shift(batch_size)
|
24
|
+
end
|
25
|
+
x_batch = @x_datas[batch_indexes, false]
|
26
|
+
y_batch = @y_datas[batch_indexes, false]
|
27
|
+
[x_batch, y_batch]
|
28
|
+
end
|
29
|
+
|
30
|
+
private def reset_indexs
|
31
|
+
@indexes = @num_datas.times.to_a
|
32
|
+
@indexes.shuffle! if @random
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module DNN
|
2
|
+
module Layers
|
3
|
+
|
4
|
+
class Embedding < HasParamLayer
|
5
|
+
# @return [Integer] Return the input length.
|
6
|
+
attr_reader :input_length
|
7
|
+
# @return [Initializers::Initializer] Return the weight initializer.
|
8
|
+
attr_reader :weight_initializer
|
9
|
+
|
10
|
+
def self.from_hash(hash)
|
11
|
+
self.new(hash[:input_shape], hash[:input_length],
|
12
|
+
weight_initializer: DNN::Utils.from_hash(hash[:weight_initializer]))
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param [Integer | Array] input_dim_or_shape Set input data dimension or shape.
|
16
|
+
# @param [Integer] input_length input Set the time series length of input data.
|
17
|
+
def initialize(input_dim_or_shape, input_length, weight_initializer: Initializers::RandomUniform.new)
|
18
|
+
super()
|
19
|
+
@input_shape = input_dim_or_shape.is_a?(Array) ? input_dim_or_shape : [input_dim_or_shape]
|
20
|
+
@input_length = input_length
|
21
|
+
@weight_initializer = weight_initializer
|
22
|
+
end
|
23
|
+
|
24
|
+
def build
|
25
|
+
@built = true
|
26
|
+
@params[:weight] = @weight = Param.new(Xumo::SFloat.new(@input_length), 0)
|
27
|
+
@weight_initializer.init_param(self, @weight)
|
28
|
+
@input_shape
|
29
|
+
end
|
30
|
+
|
31
|
+
def forward(x)
|
32
|
+
@x = x
|
33
|
+
y = Xumo::SFloat.zeros(*x.shape)
|
34
|
+
x.shape[0].times do |i|
|
35
|
+
y[i, false] = @weight.data[x[i, false]]
|
36
|
+
end
|
37
|
+
y
|
38
|
+
end
|
39
|
+
|
40
|
+
def backward(dy)
|
41
|
+
@weight.grad += Xumo::SFloat.zeros(*@weight.data.shape)
|
42
|
+
@x.shape[0].times do |i|
|
43
|
+
@x.shape[1].times do |j|
|
44
|
+
@weight.grad[@x[i, j]] += dy[i, j]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_hash
|
51
|
+
super(input_shape: @input_shape, input_length: @input_length, weight_initializer: @weight_initializer.to_hash)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
data/lib/dnn/core/error.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
module DNN
|
2
|
-
class DNN_Error < StandardError; end
|
3
|
-
|
4
|
-
class DNN_ShapeError < DNN_Error; end
|
5
|
-
end
|
1
|
+
module DNN
|
2
|
+
class DNN_Error < StandardError; end
|
3
|
+
|
4
|
+
class DNN_ShapeError < DNN_Error; end
|
5
|
+
end
|
@@ -1,126 +1,126 @@
|
|
1
|
-
module DNN
|
2
|
-
module Initializers
|
3
|
-
|
4
|
-
class Initializer
|
5
|
-
def initialize(seed = false)
|
6
|
-
@seed = seed == true ? rand(1 << 31) : seed
|
7
|
-
end
|
8
|
-
|
9
|
-
def init_param(layer, param)
|
10
|
-
raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'init_params'")
|
11
|
-
end
|
12
|
-
|
13
|
-
def to_hash(merge_hash = nil)
|
14
|
-
hash = {class: self.class.name, seed: @seed}
|
15
|
-
hash.merge!(merge_hash) if merge_hash
|
16
|
-
hash
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
|
21
|
-
class Zeros < Initializer
|
22
|
-
def init_param(layer, param)
|
23
|
-
param.data = param.data.fill(0)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
|
28
|
-
class Const < Initializer
|
29
|
-
attr_reader :const
|
30
|
-
|
31
|
-
def self.from_hash(hash)
|
32
|
-
self.new(hash[:const])
|
33
|
-
end
|
34
|
-
|
35
|
-
def initialize(const)
|
36
|
-
super()
|
37
|
-
@const = const
|
38
|
-
end
|
39
|
-
|
40
|
-
def init_param(layer, param)
|
41
|
-
param.data = param.data.fill(@const)
|
42
|
-
end
|
43
|
-
|
44
|
-
def to_hash
|
45
|
-
super({const: @const})
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
|
50
|
-
class RandomNormal < Initializer
|
51
|
-
attr_reader :mean
|
52
|
-
attr_reader :std
|
53
|
-
|
54
|
-
def self.from_hash(hash)
|
55
|
-
self.new(hash[:mean], hash[:std], hash[:seed])
|
56
|
-
end
|
57
|
-
|
58
|
-
def initialize(mean = 0, std = 0.05, seed = true)
|
59
|
-
super(seed)
|
60
|
-
@mean = mean
|
61
|
-
@std = std
|
62
|
-
end
|
63
|
-
|
64
|
-
def init_param(layer, param)
|
65
|
-
Xumo::SFloat.srand(@seed)
|
66
|
-
param.data = param.data.rand_norm(@mean, @std)
|
67
|
-
end
|
68
|
-
|
69
|
-
def to_hash
|
70
|
-
super({mean: @mean, std: @std})
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
|
75
|
-
class RandomUniform < Initializer
|
76
|
-
attr_reader :min
|
77
|
-
attr_reader :max
|
78
|
-
|
79
|
-
def self.from_hash(hash)
|
80
|
-
self.new(hash[:min], hash[:max], hash[:seed])
|
81
|
-
end
|
82
|
-
|
83
|
-
def initialize(min = -0.05, max = 0.05, seed = true)
|
84
|
-
super(seed)
|
85
|
-
@min = min
|
86
|
-
@max = max
|
87
|
-
end
|
88
|
-
|
89
|
-
def init_param(layer, param)
|
90
|
-
Xumo::SFloat.srand(@seed)
|
91
|
-
param.data = param.data.rand(@min, @max)
|
92
|
-
end
|
93
|
-
|
94
|
-
def to_hash
|
95
|
-
super({min: @min, max: @max})
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
|
100
|
-
class Xavier < Initializer
|
101
|
-
def initialize(seed = true)
|
102
|
-
super
|
103
|
-
end
|
104
|
-
|
105
|
-
def init_param(layer, param)
|
106
|
-
Xumo::SFloat.srand(@seed)
|
107
|
-
num_prev_nodes = layer.input_shape.reduce(:*)
|
108
|
-
param.data = param.data.rand_norm / Math.sqrt(num_prev_nodes)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
|
113
|
-
class He < Initializer
|
114
|
-
def initialize(seed = true)
|
115
|
-
super
|
116
|
-
end
|
117
|
-
|
118
|
-
def init_param(layer, param)
|
119
|
-
Xumo::SFloat.srand(@seed)
|
120
|
-
num_prev_nodes = layer.input_shape.reduce(:*)
|
121
|
-
param.data = param.data.rand_norm / Math.sqrt(num_prev_nodes) * Math.sqrt(2)
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
end
|
126
|
-
end
|
1
|
+
module DNN
|
2
|
+
module Initializers
|
3
|
+
|
4
|
+
class Initializer
|
5
|
+
def initialize(seed = false)
|
6
|
+
@seed = seed == true ? rand(1 << 31) : seed
|
7
|
+
end
|
8
|
+
|
9
|
+
def init_param(layer, param)
|
10
|
+
raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'init_params'")
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_hash(merge_hash = nil)
|
14
|
+
hash = {class: self.class.name, seed: @seed}
|
15
|
+
hash.merge!(merge_hash) if merge_hash
|
16
|
+
hash
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
class Zeros < Initializer
|
22
|
+
def init_param(layer, param)
|
23
|
+
param.data = param.data.fill(0)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
class Const < Initializer
|
29
|
+
attr_reader :const
|
30
|
+
|
31
|
+
def self.from_hash(hash)
|
32
|
+
self.new(hash[:const])
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(const)
|
36
|
+
super()
|
37
|
+
@const = const
|
38
|
+
end
|
39
|
+
|
40
|
+
def init_param(layer, param)
|
41
|
+
param.data = param.data.fill(@const)
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_hash
|
45
|
+
super({const: @const})
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
class RandomNormal < Initializer
|
51
|
+
attr_reader :mean
|
52
|
+
attr_reader :std
|
53
|
+
|
54
|
+
def self.from_hash(hash)
|
55
|
+
self.new(hash[:mean], hash[:std], hash[:seed])
|
56
|
+
end
|
57
|
+
|
58
|
+
def initialize(mean = 0, std = 0.05, seed = true)
|
59
|
+
super(seed)
|
60
|
+
@mean = mean
|
61
|
+
@std = std
|
62
|
+
end
|
63
|
+
|
64
|
+
def init_param(layer, param)
|
65
|
+
Xumo::SFloat.srand(@seed)
|
66
|
+
param.data = param.data.rand_norm(@mean, @std)
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_hash
|
70
|
+
super({mean: @mean, std: @std})
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
class RandomUniform < Initializer
|
76
|
+
attr_reader :min
|
77
|
+
attr_reader :max
|
78
|
+
|
79
|
+
def self.from_hash(hash)
|
80
|
+
self.new(hash[:min], hash[:max], hash[:seed])
|
81
|
+
end
|
82
|
+
|
83
|
+
def initialize(min = -0.05, max = 0.05, seed = true)
|
84
|
+
super(seed)
|
85
|
+
@min = min
|
86
|
+
@max = max
|
87
|
+
end
|
88
|
+
|
89
|
+
def init_param(layer, param)
|
90
|
+
Xumo::SFloat.srand(@seed)
|
91
|
+
param.data = param.data.rand(@min, @max)
|
92
|
+
end
|
93
|
+
|
94
|
+
def to_hash
|
95
|
+
super({min: @min, max: @max})
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
class Xavier < Initializer
|
101
|
+
def initialize(seed = true)
|
102
|
+
super
|
103
|
+
end
|
104
|
+
|
105
|
+
def init_param(layer, param)
|
106
|
+
Xumo::SFloat.srand(@seed)
|
107
|
+
num_prev_nodes = layer.input_shape.reduce(:*)
|
108
|
+
param.data = param.data.rand_norm / Math.sqrt(num_prev_nodes)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
class He < Initializer
|
114
|
+
def initialize(seed = true)
|
115
|
+
super
|
116
|
+
end
|
117
|
+
|
118
|
+
def init_param(layer, param)
|
119
|
+
Xumo::SFloat.srand(@seed)
|
120
|
+
num_prev_nodes = layer.input_shape.reduce(:*)
|
121
|
+
param.data = param.data.rand_norm / Math.sqrt(num_prev_nodes) * Math.sqrt(2)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
end
|
126
|
+
end
|
data/lib/dnn/core/layers.rb
CHANGED
@@ -1,307 +1,307 @@
|
|
1
|
-
module DNN
|
2
|
-
module Layers
|
3
|
-
|
4
|
-
# Super class of all optimizer classes.
|
5
|
-
class Layer
|
6
|
-
# @return [Bool] learning_phase Return the true if learning.
|
7
|
-
attr_accessor :learning_phase
|
8
|
-
# @return [Array] Return the shape of the input data.
|
9
|
-
attr_reader :input_shape
|
10
|
-
|
11
|
-
def initialize
|
12
|
-
@built = false
|
13
|
-
end
|
14
|
-
|
15
|
-
# Build the layer.
|
16
|
-
# @param [Array] input_shape Setting the shape of the input data.
|
17
|
-
def build(input_shape)
|
18
|
-
@input_shape = input_shape
|
19
|
-
@learning_phase = true
|
20
|
-
@built = true
|
21
|
-
end
|
22
|
-
|
23
|
-
# Does the layer have already been built?
|
24
|
-
# @return [Bool] If layer have already been built then return true.
|
25
|
-
def built?
|
26
|
-
@built
|
27
|
-
end
|
28
|
-
|
29
|
-
# Forward propagation.
|
30
|
-
def forward(x)
|
31
|
-
raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'forward'")
|
32
|
-
end
|
33
|
-
|
34
|
-
# Backward propagation.
|
35
|
-
def backward(dy)
|
36
|
-
raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'backward'")
|
37
|
-
end
|
38
|
-
|
39
|
-
# Please reimplement this method as needed.
|
40
|
-
# The default implementation return input_shape.
|
41
|
-
# @return [Array] Return the shape of the output data.
|
42
|
-
def output_shape
|
43
|
-
@input_shape
|
44
|
-
end
|
45
|
-
|
46
|
-
# Layer to a hash.
|
47
|
-
def to_hash(merge_hash = nil)
|
48
|
-
hash = {class: self.class.name}
|
49
|
-
hash.merge!(merge_hash) if merge_hash
|
50
|
-
hash
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
|
55
|
-
# This class is a superclass of all classes with learning parameters.
|
56
|
-
class HasParamLayer < Layer
|
57
|
-
# @return [Bool] trainable Setting false prevents learning of parameters.
|
58
|
-
attr_accessor :trainable
|
59
|
-
# @return [Array] The parameters of the layer.
|
60
|
-
attr_reader :params
|
61
|
-
|
62
|
-
def initialize
|
63
|
-
super()
|
64
|
-
@params = {}
|
65
|
-
@trainable = true
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
|
70
|
-
class InputLayer < Layer
|
71
|
-
def self.from_hash(hash)
|
72
|
-
self.new(hash[:input_shape])
|
73
|
-
end
|
74
|
-
|
75
|
-
def initialize(input_dim_or_shape)
|
76
|
-
super()
|
77
|
-
@input_shape = input_dim_or_shape.is_a?(Array) ? input_dim_or_shape : [input_dim_or_shape]
|
78
|
-
end
|
79
|
-
|
80
|
-
def build
|
81
|
-
@built = true
|
82
|
-
@input_shape
|
83
|
-
end
|
84
|
-
|
85
|
-
def forward(x)
|
86
|
-
x
|
87
|
-
end
|
88
|
-
|
89
|
-
def backward(dy)
|
90
|
-
dy
|
91
|
-
end
|
92
|
-
|
93
|
-
def to_hash
|
94
|
-
super({input_shape: @input_shape})
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
|
99
|
-
# It is a superclass of all connection layers.
|
100
|
-
class Connection < HasParamLayer
|
101
|
-
# @return [DNN::Initializers::Initializer] Weight initializer.
|
102
|
-
attr_reader :weight_initializer
|
103
|
-
# @return [DNN::Initializers::Initializer] Bias initializer.
|
104
|
-
attr_reader :bias_initializer
|
105
|
-
# @return [DNN::Regularizers::Regularizer] Weight regularization.
|
106
|
-
attr_reader :weight_regularizer
|
107
|
-
# @return [DNN::Regularizers::Regularizer] Bias regularization.
|
108
|
-
attr_reader :bias_regularizer
|
109
|
-
|
110
|
-
# @param [DNN::Initializers::Initializer] weight_initializer Weight initializer.
|
111
|
-
# @param [DNN::Initializers::Initializer] bias_initializer Bias initializer.
|
112
|
-
# @param [DNN::Regularizers::Regularizer] weight_regularizer Weight regularization.
|
113
|
-
# @param [DNN::Regularizers::Regularizer] bias_regularizer Bias regularization.
|
114
|
-
# @param [Bool] use_bias whether to use bias.
|
115
|
-
def initialize(weight_initializer: Initializers::RandomNormal.new,
|
116
|
-
bias_initializer: Initializers::Zeros.new,
|
117
|
-
weight_regularizer: nil,
|
118
|
-
bias_regularizer: nil,
|
119
|
-
use_bias: true)
|
120
|
-
super()
|
121
|
-
@weight_initializer = weight_initializer
|
122
|
-
@bias_initializer = bias_initializer
|
123
|
-
@weight_regularizer = weight_regularizer
|
124
|
-
@bias_regularizer = bias_regularizer
|
125
|
-
@params[:weight] = @weight = Param.new(nil, 0)
|
126
|
-
if use_bias
|
127
|
-
@params[:bias] = @bias = Param.new(nil, 0)
|
128
|
-
else
|
129
|
-
@bias = nil
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
def regularizers
|
134
|
-
regularizers = []
|
135
|
-
regularizers << @weight_regularizer if @weight_regularizer
|
136
|
-
regularizers << @bias_regularizer if @bias_regularizer
|
137
|
-
regularizers
|
138
|
-
end
|
139
|
-
|
140
|
-
# @return [Bool] Return whether to use bias.
|
141
|
-
def use_bias
|
142
|
-
@bias ? true : false
|
143
|
-
end
|
144
|
-
|
145
|
-
def to_hash(merge_hash)
|
146
|
-
super({weight_initializer: @weight_initializer.to_hash,
|
147
|
-
bias_initializer: @bias_initializer.to_hash,
|
148
|
-
weight_regularizer: @weight_regularizer&.to_hash,
|
149
|
-
bias_regularizer: @bias_regularizer&.to_hash,
|
150
|
-
use_bias: use_bias}.merge(merge_hash))
|
151
|
-
end
|
152
|
-
|
153
|
-
private def init_weight_and_bias
|
154
|
-
@weight_initializer.init_param(self, @weight)
|
155
|
-
@weight_regularizer.param = @weight if @weight_regularizer
|
156
|
-
if @bias
|
157
|
-
@bias_initializer.init_param(self, @bias)
|
158
|
-
@bias_regularizer.param = @bias if @bias_regularizer
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
|
164
|
-
# Full connnection layer.
|
165
|
-
class Dense < Connection
|
166
|
-
# @return [Integer] number of nodes.
|
167
|
-
attr_reader :num_nodes
|
168
|
-
|
169
|
-
def self.from_hash(hash)
|
170
|
-
self.new(hash[:num_nodes],
|
171
|
-
weight_initializer: Utils.from_hash(hash[:weight_initializer]),
|
172
|
-
bias_initializer: Utils.from_hash(hash[:bias_initializer]),
|
173
|
-
weight_regularizer: Utils.from_hash(hash[:weight_regularizer]),
|
174
|
-
bias_regularizer: Utils.from_hash(hash[:bias_regularizer]),
|
175
|
-
use_bias: hash[:use_bias])
|
176
|
-
end
|
177
|
-
|
178
|
-
# @param [Integer] num_nodes number of nodes.
|
179
|
-
def initialize(num_nodes,
|
180
|
-
weight_initializer: Initializers::RandomNormal.new,
|
181
|
-
bias_initializer: Initializers::Zeros.new,
|
182
|
-
weight_regularizer: nil,
|
183
|
-
bias_regularizer: nil,
|
184
|
-
use_bias: true)
|
185
|
-
super(weight_initializer: weight_initializer, bias_initializer: bias_initializer,
|
186
|
-
weight_regularizer: weight_regularizer, bias_regularizer: bias_regularizer, use_bias: use_bias)
|
187
|
-
@num_nodes = num_nodes
|
188
|
-
end
|
189
|
-
|
190
|
-
def build(input_shape)
|
191
|
-
super
|
192
|
-
num_prev_nodes = input_shape[0]
|
193
|
-
@weight.data = Xumo::SFloat.new(num_prev_nodes, @num_nodes)
|
194
|
-
@bias.data = Xumo::SFloat.new(@num_nodes) if @bias
|
195
|
-
init_weight_and_bias
|
196
|
-
end
|
197
|
-
|
198
|
-
def forward(x)
|
199
|
-
@x = x
|
200
|
-
y = x.dot(@weight.data)
|
201
|
-
y += @bias.data if @bias
|
202
|
-
y
|
203
|
-
end
|
204
|
-
|
205
|
-
def backward(dy)
|
206
|
-
if @trainable
|
207
|
-
@weight.grad += @x.transpose.dot(dy)
|
208
|
-
@bias.grad += dy.sum(0) if @bias
|
209
|
-
end
|
210
|
-
dy.dot(@weight.data.transpose)
|
211
|
-
end
|
212
|
-
|
213
|
-
def output_shape
|
214
|
-
[@num_nodes]
|
215
|
-
end
|
216
|
-
|
217
|
-
def to_hash
|
218
|
-
super({num_nodes: @num_nodes})
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
|
223
|
-
class Flatten < Layer
|
224
|
-
def forward(x)
|
225
|
-
x.reshape(x.shape[0], *output_shape)
|
226
|
-
end
|
227
|
-
|
228
|
-
def backward(dy)
|
229
|
-
dy.reshape(dy.shape[0], *@input_shape)
|
230
|
-
end
|
231
|
-
|
232
|
-
def output_shape
|
233
|
-
[@input_shape.reduce(:*)]
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
|
238
|
-
class Reshape < Layer
|
239
|
-
def self.from_hash(hash)
|
240
|
-
self.new(hash[:output_shape])
|
241
|
-
end
|
242
|
-
|
243
|
-
def initialize(output_shape)
|
244
|
-
super()
|
245
|
-
@output_shape = output_shape
|
246
|
-
end
|
247
|
-
|
248
|
-
def forward(x)
|
249
|
-
x.reshape(x.shape[0], *@output_shape)
|
250
|
-
end
|
251
|
-
|
252
|
-
def backward(dy)
|
253
|
-
dy.reshape(dy.shape[0], *@input_shape)
|
254
|
-
end
|
255
|
-
|
256
|
-
def output_shape
|
257
|
-
@output_shape
|
258
|
-
end
|
259
|
-
|
260
|
-
def to_hash
|
261
|
-
super({output_shape: @output_shape})
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
|
266
|
-
class Dropout < Layer
|
267
|
-
# @return [Float] dropout ratio.
|
268
|
-
attr_accessor :dropout_ratio
|
269
|
-
# @return [Float] Use 'weight scaling inference rule'.
|
270
|
-
attr_reader :use_scale
|
271
|
-
|
272
|
-
def self.from_hash(hash)
|
273
|
-
self.new(hash[:dropout_ratio], seed: hash[:seed], use_scale: hash[:use_scale])
|
274
|
-
end
|
275
|
-
|
276
|
-
def initialize(dropout_ratio = 0.5, seed: rand(1 << 31), use_scale: true)
|
277
|
-
super()
|
278
|
-
@dropout_ratio = dropout_ratio
|
279
|
-
@seed = seed
|
280
|
-
@use_scale = use_scale
|
281
|
-
@mask = nil
|
282
|
-
end
|
283
|
-
|
284
|
-
def forward(x)
|
285
|
-
if learning_phase
|
286
|
-
Xumo::SFloat.srand(@seed)
|
287
|
-
@mask = Xumo::SFloat.ones(*x.shape).rand < @dropout_ratio
|
288
|
-
x[@mask] = 0
|
289
|
-
else
|
290
|
-
x *= (1 - @dropout_ratio) if @use_scale
|
291
|
-
end
|
292
|
-
x
|
293
|
-
end
|
294
|
-
|
295
|
-
def backward(dy)
|
296
|
-
dy[@mask] = 0
|
297
|
-
dy
|
298
|
-
end
|
299
|
-
|
300
|
-
def to_hash
|
301
|
-
super({dropout_ratio: @dropout_ratio, seed: @seed, use_scale: @use_scale})
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
end
|
306
|
-
|
307
|
-
end
|
1
|
+
module DNN
|
2
|
+
module Layers
|
3
|
+
|
4
|
+
# Super class of all optimizer classes.
|
5
|
+
class Layer
|
6
|
+
# @return [Bool] learning_phase Return the true if learning.
|
7
|
+
attr_accessor :learning_phase
|
8
|
+
# @return [Array] Return the shape of the input data.
|
9
|
+
attr_reader :input_shape
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@built = false
|
13
|
+
end
|
14
|
+
|
15
|
+
# Build the layer.
|
16
|
+
# @param [Array] input_shape Setting the shape of the input data.
|
17
|
+
def build(input_shape)
|
18
|
+
@input_shape = input_shape
|
19
|
+
@learning_phase = true
|
20
|
+
@built = true
|
21
|
+
end
|
22
|
+
|
23
|
+
# Does the layer have already been built?
|
24
|
+
# @return [Bool] If layer have already been built then return true.
|
25
|
+
def built?
|
26
|
+
@built
|
27
|
+
end
|
28
|
+
|
29
|
+
# Forward propagation.
|
30
|
+
def forward(x)
|
31
|
+
raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'forward'")
|
32
|
+
end
|
33
|
+
|
34
|
+
# Backward propagation.
|
35
|
+
def backward(dy)
|
36
|
+
raise NotImplementedError.new("Class '#{self.class.name}' has implement method 'backward'")
|
37
|
+
end
|
38
|
+
|
39
|
+
# Please reimplement this method as needed.
|
40
|
+
# The default implementation return input_shape.
|
41
|
+
# @return [Array] Return the shape of the output data.
|
42
|
+
def output_shape
|
43
|
+
@input_shape
|
44
|
+
end
|
45
|
+
|
46
|
+
# Layer to a hash.
|
47
|
+
def to_hash(merge_hash = nil)
|
48
|
+
hash = {class: self.class.name}
|
49
|
+
hash.merge!(merge_hash) if merge_hash
|
50
|
+
hash
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
# This class is a superclass of all classes with learning parameters.
|
56
|
+
class HasParamLayer < Layer
|
57
|
+
# @return [Bool] trainable Setting false prevents learning of parameters.
|
58
|
+
attr_accessor :trainable
|
59
|
+
# @return [Array] The parameters of the layer.
|
60
|
+
attr_reader :params
|
61
|
+
|
62
|
+
def initialize
|
63
|
+
super()
|
64
|
+
@params = {}
|
65
|
+
@trainable = true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
class InputLayer < Layer
|
71
|
+
def self.from_hash(hash)
|
72
|
+
self.new(hash[:input_shape])
|
73
|
+
end
|
74
|
+
|
75
|
+
def initialize(input_dim_or_shape)
|
76
|
+
super()
|
77
|
+
@input_shape = input_dim_or_shape.is_a?(Array) ? input_dim_or_shape : [input_dim_or_shape]
|
78
|
+
end
|
79
|
+
|
80
|
+
def build
|
81
|
+
@built = true
|
82
|
+
@input_shape
|
83
|
+
end
|
84
|
+
|
85
|
+
def forward(x)
|
86
|
+
x
|
87
|
+
end
|
88
|
+
|
89
|
+
def backward(dy)
|
90
|
+
dy
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_hash
|
94
|
+
super({input_shape: @input_shape})
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
# It is a superclass of all connection layers.
|
100
|
+
class Connection < HasParamLayer
|
101
|
+
# @return [DNN::Initializers::Initializer] Weight initializer.
|
102
|
+
attr_reader :weight_initializer
|
103
|
+
# @return [DNN::Initializers::Initializer] Bias initializer.
|
104
|
+
attr_reader :bias_initializer
|
105
|
+
# @return [DNN::Regularizers::Regularizer] Weight regularization.
|
106
|
+
attr_reader :weight_regularizer
|
107
|
+
# @return [DNN::Regularizers::Regularizer] Bias regularization.
|
108
|
+
attr_reader :bias_regularizer
|
109
|
+
|
110
|
+
# @param [DNN::Initializers::Initializer] weight_initializer Weight initializer.
|
111
|
+
# @param [DNN::Initializers::Initializer] bias_initializer Bias initializer.
|
112
|
+
# @param [DNN::Regularizers::Regularizer] weight_regularizer Weight regularization.
|
113
|
+
# @param [DNN::Regularizers::Regularizer] bias_regularizer Bias regularization.
|
114
|
+
# @param [Bool] use_bias whether to use bias.
|
115
|
+
def initialize(weight_initializer: Initializers::RandomNormal.new,
|
116
|
+
bias_initializer: Initializers::Zeros.new,
|
117
|
+
weight_regularizer: nil,
|
118
|
+
bias_regularizer: nil,
|
119
|
+
use_bias: true)
|
120
|
+
super()
|
121
|
+
@weight_initializer = weight_initializer
|
122
|
+
@bias_initializer = bias_initializer
|
123
|
+
@weight_regularizer = weight_regularizer
|
124
|
+
@bias_regularizer = bias_regularizer
|
125
|
+
@params[:weight] = @weight = Param.new(nil, 0)
|
126
|
+
if use_bias
|
127
|
+
@params[:bias] = @bias = Param.new(nil, 0)
|
128
|
+
else
|
129
|
+
@bias = nil
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def regularizers
|
134
|
+
regularizers = []
|
135
|
+
regularizers << @weight_regularizer if @weight_regularizer
|
136
|
+
regularizers << @bias_regularizer if @bias_regularizer
|
137
|
+
regularizers
|
138
|
+
end
|
139
|
+
|
140
|
+
# @return [Bool] Return whether to use bias.
|
141
|
+
def use_bias
|
142
|
+
@bias ? true : false
|
143
|
+
end
|
144
|
+
|
145
|
+
def to_hash(merge_hash)
|
146
|
+
super({weight_initializer: @weight_initializer.to_hash,
|
147
|
+
bias_initializer: @bias_initializer.to_hash,
|
148
|
+
weight_regularizer: @weight_regularizer&.to_hash,
|
149
|
+
bias_regularizer: @bias_regularizer&.to_hash,
|
150
|
+
use_bias: use_bias}.merge(merge_hash))
|
151
|
+
end
|
152
|
+
|
153
|
+
private def init_weight_and_bias
|
154
|
+
@weight_initializer.init_param(self, @weight)
|
155
|
+
@weight_regularizer.param = @weight if @weight_regularizer
|
156
|
+
if @bias
|
157
|
+
@bias_initializer.init_param(self, @bias)
|
158
|
+
@bias_regularizer.param = @bias if @bias_regularizer
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
# Full connnection layer.
|
165
|
+
class Dense < Connection
|
166
|
+
# @return [Integer] number of nodes.
|
167
|
+
attr_reader :num_nodes
|
168
|
+
|
169
|
+
def self.from_hash(hash)
|
170
|
+
self.new(hash[:num_nodes],
|
171
|
+
weight_initializer: Utils.from_hash(hash[:weight_initializer]),
|
172
|
+
bias_initializer: Utils.from_hash(hash[:bias_initializer]),
|
173
|
+
weight_regularizer: Utils.from_hash(hash[:weight_regularizer]),
|
174
|
+
bias_regularizer: Utils.from_hash(hash[:bias_regularizer]),
|
175
|
+
use_bias: hash[:use_bias])
|
176
|
+
end
|
177
|
+
|
178
|
+
# @param [Integer] num_nodes number of nodes.
|
179
|
+
def initialize(num_nodes,
|
180
|
+
weight_initializer: Initializers::RandomNormal.new,
|
181
|
+
bias_initializer: Initializers::Zeros.new,
|
182
|
+
weight_regularizer: nil,
|
183
|
+
bias_regularizer: nil,
|
184
|
+
use_bias: true)
|
185
|
+
super(weight_initializer: weight_initializer, bias_initializer: bias_initializer,
|
186
|
+
weight_regularizer: weight_regularizer, bias_regularizer: bias_regularizer, use_bias: use_bias)
|
187
|
+
@num_nodes = num_nodes
|
188
|
+
end
|
189
|
+
|
190
|
+
def build(input_shape)
|
191
|
+
super
|
192
|
+
num_prev_nodes = input_shape[0]
|
193
|
+
@weight.data = Xumo::SFloat.new(num_prev_nodes, @num_nodes)
|
194
|
+
@bias.data = Xumo::SFloat.new(@num_nodes) if @bias
|
195
|
+
init_weight_and_bias
|
196
|
+
end
|
197
|
+
|
198
|
+
def forward(x)
|
199
|
+
@x = x
|
200
|
+
y = x.dot(@weight.data)
|
201
|
+
y += @bias.data if @bias
|
202
|
+
y
|
203
|
+
end
|
204
|
+
|
205
|
+
def backward(dy)
|
206
|
+
if @trainable
|
207
|
+
@weight.grad += @x.transpose.dot(dy)
|
208
|
+
@bias.grad += dy.sum(0) if @bias
|
209
|
+
end
|
210
|
+
dy.dot(@weight.data.transpose)
|
211
|
+
end
|
212
|
+
|
213
|
+
def output_shape
|
214
|
+
[@num_nodes]
|
215
|
+
end
|
216
|
+
|
217
|
+
def to_hash
|
218
|
+
super({num_nodes: @num_nodes})
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
class Flatten < Layer
|
224
|
+
def forward(x)
|
225
|
+
x.reshape(x.shape[0], *output_shape)
|
226
|
+
end
|
227
|
+
|
228
|
+
def backward(dy)
|
229
|
+
dy.reshape(dy.shape[0], *@input_shape)
|
230
|
+
end
|
231
|
+
|
232
|
+
def output_shape
|
233
|
+
[@input_shape.reduce(:*)]
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
|
238
|
+
class Reshape < Layer
|
239
|
+
def self.from_hash(hash)
|
240
|
+
self.new(hash[:output_shape])
|
241
|
+
end
|
242
|
+
|
243
|
+
def initialize(output_shape)
|
244
|
+
super()
|
245
|
+
@output_shape = output_shape
|
246
|
+
end
|
247
|
+
|
248
|
+
def forward(x)
|
249
|
+
x.reshape(x.shape[0], *@output_shape)
|
250
|
+
end
|
251
|
+
|
252
|
+
def backward(dy)
|
253
|
+
dy.reshape(dy.shape[0], *@input_shape)
|
254
|
+
end
|
255
|
+
|
256
|
+
def output_shape
|
257
|
+
@output_shape
|
258
|
+
end
|
259
|
+
|
260
|
+
def to_hash
|
261
|
+
super({output_shape: @output_shape})
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
|
266
|
+
class Dropout < Layer
|
267
|
+
# @return [Float] dropout ratio.
|
268
|
+
attr_accessor :dropout_ratio
|
269
|
+
# @return [Float] Use 'weight scaling inference rule'.
|
270
|
+
attr_reader :use_scale
|
271
|
+
|
272
|
+
def self.from_hash(hash)
|
273
|
+
self.new(hash[:dropout_ratio], seed: hash[:seed], use_scale: hash[:use_scale])
|
274
|
+
end
|
275
|
+
|
276
|
+
def initialize(dropout_ratio = 0.5, seed: rand(1 << 31), use_scale: true)
|
277
|
+
super()
|
278
|
+
@dropout_ratio = dropout_ratio
|
279
|
+
@seed = seed
|
280
|
+
@use_scale = use_scale
|
281
|
+
@mask = nil
|
282
|
+
end
|
283
|
+
|
284
|
+
def forward(x)
|
285
|
+
if learning_phase
|
286
|
+
Xumo::SFloat.srand(@seed)
|
287
|
+
@mask = Xumo::SFloat.ones(*x.shape).rand < @dropout_ratio
|
288
|
+
x[@mask] = 0
|
289
|
+
else
|
290
|
+
x *= (1 - @dropout_ratio) if @use_scale
|
291
|
+
end
|
292
|
+
x
|
293
|
+
end
|
294
|
+
|
295
|
+
def backward(dy)
|
296
|
+
dy[@mask] = 0
|
297
|
+
dy
|
298
|
+
end
|
299
|
+
|
300
|
+
def to_hash
|
301
|
+
super({dropout_ratio: @dropout_ratio, seed: @seed, use_scale: @use_scale})
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|