machine_learning_workbench 0.2.0 → 0.2.1
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/lib/machine_learning_workbench/neural_network/base.rb +14 -13
- data/lib/machine_learning_workbench/neural_network/feed_forward.rb +2 -2
- data/lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb +17 -12
- data/lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb +2 -2
- data/lib/machine_learning_workbench/optimizer/natural_evolution_strategies/snes.rb +7 -7
- data/lib/machine_learning_workbench/optimizer/natural_evolution_strategies/xnes.rb +5 -5
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f393f2183c3371081f694e47e35a14cf93997098
|
4
|
+
data.tar.gz: 754c861e440af0a40a5e328dfdde143a5e1bff59
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9ed7f6be2d1ed63dd00f26dc8d4b4e47c3ece23c80192bc877c4acaa1c03e1f37a34a64e32b7c6f4af2993439492b39e20f17110f50a14add762345685485fff
|
7
|
+
data.tar.gz: e6d116d1a8011da42a24ad3b10209e3764cb3195e6f3149659b9d91a637a029b0ffe9b50f47f7cbed4fd9970ceb7a112884b54f0c566561b9121c434e1455d4c
|
@@ -17,23 +17,24 @@ module MachineLearningWorkbench::NeuralNetwork
|
|
17
17
|
# @!attribute [r] struct
|
18
18
|
# list of number of (inputs or) neurons in each layer
|
19
19
|
# @return [Array<Integer>] structure of the network
|
20
|
-
attr_reader :layers, :state, :act_fn, :struct
|
20
|
+
attr_reader :layers, :state, :act_fn, :struct, :dtype
|
21
21
|
|
22
22
|
|
23
23
|
## Initialization
|
24
24
|
|
25
25
|
# @param struct [Array<Integer>] list of layer sizes
|
26
26
|
# @param act_fn [Symbol] choice of activation function for the neurons
|
27
|
-
|
27
|
+
# @param dtype [NMatrix dtype] NMatrix dtype for weights and states
|
28
|
+
def initialize struct, act_fn: nil, dtype: :float32
|
28
29
|
@struct = struct
|
29
|
-
@act_fn = self.
|
30
|
+
@act_fn = self.get_act_fn(act_fn || :sigmoid)
|
30
31
|
# @state holds both inputs, possibly recurrency, and bias
|
31
32
|
# it is a complete input for the next layer, hence size from layer sizes
|
32
33
|
@state = layer_row_sizes.collect do |size|
|
33
|
-
NMatrix.zeros([1, size], dtype:
|
34
|
+
NMatrix.zeros([1, size], dtype: dtype)
|
34
35
|
end
|
35
36
|
# to this, append a matrix to hold the final network output
|
36
|
-
@state.push NMatrix.zeros([1, nneurs(-1)], dtype:
|
37
|
+
@state.push NMatrix.zeros([1, nneurs(-1)], dtype: dtype)
|
37
38
|
reset_state
|
38
39
|
end
|
39
40
|
|
@@ -126,7 +127,7 @@ module MachineLearningWorkbench::NeuralNetwork
|
|
126
127
|
raise ArgumentError unless weights.size == nweights
|
127
128
|
weights_iter = weights.each
|
128
129
|
@layers = layer_shapes.collect do |shape|
|
129
|
-
NMatrix.new(shape, dtype:
|
130
|
+
NMatrix.new(shape, dtype: dtype) { weights_iter.next }
|
130
131
|
end
|
131
132
|
reset_state
|
132
133
|
return true
|
@@ -137,7 +138,7 @@ module MachineLearningWorkbench::NeuralNetwork
|
|
137
138
|
|
138
139
|
# The "fixed `1`" used in the layer's input
|
139
140
|
def bias
|
140
|
-
@bias ||= NMatrix[[1], dtype:
|
141
|
+
@bias ||= NMatrix[[1], dtype: dtype]
|
141
142
|
end
|
142
143
|
|
143
144
|
# Activate the network on a given input
|
@@ -168,10 +169,10 @@ module MachineLearningWorkbench::NeuralNetwork
|
|
168
169
|
|
169
170
|
# Activation function caller. Allows to cleanly define the activation function as one-dimensional, by calling it over the inputs and building a NMatrix to return.
|
170
171
|
# @return [NMatrix] activations for one layer
|
171
|
-
def
|
172
|
+
def get_act_fn type, *args
|
172
173
|
fn = send(type,*args)
|
173
174
|
lambda do |inputs|
|
174
|
-
NMatrix.new([1, inputs.size], dtype:
|
175
|
+
NMatrix.new([1, inputs.size], dtype: dtype) do |_,i|
|
175
176
|
# single-row matrix, indices are columns
|
176
177
|
fn.call inputs[i]
|
177
178
|
end
|
@@ -179,14 +180,14 @@ module MachineLearningWorkbench::NeuralNetwork
|
|
179
180
|
end
|
180
181
|
|
181
182
|
# Traditional sigmoid with variable steepness
|
182
|
-
def
|
183
|
+
def sigmoid k=0.5
|
183
184
|
# k is steepness: 0<k<1 is flatter, 1<k is flatter
|
184
185
|
# flatter makes activation less sensitive, better with large number of inputs
|
185
186
|
lambda { |x| 1.0 / (Math.exp(-k * x) + 1.0) }
|
186
187
|
end
|
187
188
|
|
188
189
|
# Traditional logistic
|
189
|
-
def
|
190
|
+
def logistic
|
190
191
|
lambda { |x|
|
191
192
|
exp = Math.exp(x)
|
192
193
|
exp.infinite? ? exp : exp / (1.0 + exp)
|
@@ -195,7 +196,7 @@ module MachineLearningWorkbench::NeuralNetwork
|
|
195
196
|
|
196
197
|
# LeCun hyperbolic activation
|
197
198
|
# @see http://yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf Section 4.4
|
198
|
-
def
|
199
|
+
def lecun_hyperbolic
|
199
200
|
lambda { |x| 1.7159 * Math.tanh(2.0*x/3.0) + 1e-3*x }
|
200
201
|
end
|
201
202
|
|
@@ -208,4 +209,4 @@ module MachineLearningWorkbench::NeuralNetwork
|
|
208
209
|
end
|
209
210
|
end
|
210
211
|
end
|
211
|
-
end
|
212
|
+
end
|
@@ -13,8 +13,8 @@ module MachineLearningWorkbench::NeuralNetwork
|
|
13
13
|
# Activates a layer of the network
|
14
14
|
# @param i [Integer] the layer to activate, zero-indexed
|
15
15
|
def activate_layer i
|
16
|
-
act_fn.call(
|
16
|
+
act_fn.call(state[i].dot layers[i])
|
17
17
|
end
|
18
18
|
|
19
19
|
end
|
20
|
-
end
|
20
|
+
end
|
@@ -2,7 +2,7 @@
|
|
2
2
|
module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
3
3
|
# Natural Evolution Strategies base class
|
4
4
|
class Base
|
5
|
-
attr_reader :ndims, :mu, :sigma, :opt_type, :obj_fn, :parallel_fit, :id, :rng, :last_fits, :best, :rescale_popsize, :rescale_lrate
|
5
|
+
attr_reader :ndims, :mu, :sigma, :opt_type, :obj_fn, :parallel_fit, :id, :rng, :last_fits, :best, :rescale_popsize, :rescale_lrate, :dtype
|
6
6
|
|
7
7
|
# NES object initialization
|
8
8
|
# @param ndims [Integer] number of parameters to optimize
|
@@ -11,22 +11,27 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
11
11
|
# @param rseed [Integer] allow for deterministic execution on rseed provided
|
12
12
|
# @param mu_init [Numeric] values to initalize the distribution's mean
|
13
13
|
# @param sigma_init [Numeric] values to initialize the distribution's covariance
|
14
|
-
# @param parallel_fit [boolean] whether the `obj_fn` should be passed all the
|
15
|
-
# together. In the canonical case the fitness function always scores a
|
16
|
-
# in practical cases though it is easier to delegate the scoring
|
17
|
-
# external fitness function. Turning this to `true` will make
|
18
|
-
# Array_ of individuals to the fitness function, rather than
|
19
|
-
|
14
|
+
# @param parallel_fit [boolean] whether the `obj_fn` should be passed all the
|
15
|
+
# individuals together. In the canonical case the fitness function always scores a
|
16
|
+
# single individual; in practical cases though it is easier to delegate the scoring
|
17
|
+
# parallelization to the external fitness function. Turning this to `true` will make
|
18
|
+
# the algorithm pass _an Array_ of individuals to the fitness function, rather than
|
19
|
+
# a single instance.
|
20
|
+
# @param rescale_popsize [Float] scaling for the default population size
|
21
|
+
# @param rescale_lrate [Float] scaling for the default learning rate
|
22
|
+
# @param dtype [NMatrix dtype] NMatrix dtype for all matrix computation
|
23
|
+
def initialize ndims, obj_fn, opt_type, rseed: nil, mu_init: 0, sigma_init: 1, parallel_fit: false, rescale_popsize: 1, rescale_lrate: 1, dtype: :float64
|
20
24
|
raise ArgumentError unless [:min, :max].include? opt_type
|
21
25
|
raise ArgumentError unless obj_fn.respond_to? :call
|
22
26
|
@ndims, @opt_type, @obj_fn, @parallel_fit = ndims, opt_type, obj_fn, parallel_fit
|
23
27
|
@rescale_popsize, @rescale_lrate = rescale_popsize, rescale_lrate
|
24
|
-
@id = NMatrix.identity(ndims, dtype:
|
28
|
+
@id = NMatrix.identity(ndims, dtype: dtype)
|
25
29
|
rseed ||= Random.new_seed
|
26
30
|
# puts "NES rseed: #{s}" # currently disabled
|
27
31
|
@rng = Random.new rseed
|
28
32
|
@best = [(opt_type==:max ? -1 : 1) * Float::INFINITY, nil]
|
29
33
|
@last_fits = []
|
34
|
+
@dtype = dtype
|
30
35
|
initialize_distribution mu_init: mu_init, sigma_init: sigma_init
|
31
36
|
end
|
32
37
|
|
@@ -58,7 +63,7 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
58
63
|
total = log_range.reduce(:+)
|
59
64
|
buf = 1.0/popsize
|
60
65
|
vals = log_range.collect { |v| v / total - buf }.reverse
|
61
|
-
NMatrix[vals, dtype:
|
66
|
+
NMatrix[vals, dtype: dtype]
|
62
67
|
end
|
63
68
|
|
64
69
|
# (see #cmaes_utilities)
|
@@ -77,7 +82,7 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
77
82
|
# popsize multivariate samples of length ndims
|
78
83
|
# @return [NMatrix] standard normal samples
|
79
84
|
def standard_normal_samples
|
80
|
-
NMatrix.new([popsize, ndims], dtype:
|
85
|
+
NMatrix.new([popsize, ndims], dtype: dtype) { standard_normal_sample }
|
81
86
|
end
|
82
87
|
|
83
88
|
# Move standard normal samples to current distribution
|
@@ -85,7 +90,7 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
85
90
|
def move_inds inds
|
86
91
|
# TODO: can we reduce the transpositions?
|
87
92
|
# sigma.dot(inds.transpose).map(&mu.method(:+)).transpose
|
88
|
-
multi_mu = NMatrix[*inds.rows.times.collect {mu.to_a}, dtype:
|
93
|
+
multi_mu = NMatrix[*inds.rows.times.collect {mu.to_a}, dtype: dtype].transpose
|
89
94
|
(multi_mu + sigma.dot(inds.transpose)).transpose
|
90
95
|
# sigma.dot(inds.transpose).transpose + inds.rows.times.collect {mu.to_a}.to_nm
|
91
96
|
end
|
@@ -106,7 +111,7 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
106
111
|
this_best = sorted.last.take(2)
|
107
112
|
opt_cmp_fn = opt_type==:min ? :< : :>
|
108
113
|
@best = this_best if this_best.first.send(opt_cmp_fn, best.first)
|
109
|
-
NMatrix[*sorted.map(&:last), dtype:
|
114
|
+
NMatrix[*sorted.map(&:last), dtype: dtype]
|
110
115
|
end
|
111
116
|
|
112
117
|
# @!method interface_methods
|
@@ -72,7 +72,7 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
72
72
|
block_samples = sorted_samples.transpose
|
73
73
|
|
74
74
|
# then back to NMatrix for usage in training
|
75
|
-
block_samples.map { |sample| NMatrix[*sample, dtype:
|
75
|
+
block_samples.map { |sample| NMatrix[*sample, dtype: dtype] }
|
76
76
|
end
|
77
77
|
|
78
78
|
# duck-type the interface: [:train, :mu, :convergence, :save, :load]
|
@@ -105,4 +105,4 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
105
105
|
end
|
106
106
|
end
|
107
107
|
end
|
108
|
-
end
|
108
|
+
end
|
@@ -6,10 +6,10 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
6
6
|
attr_reader :variances
|
7
7
|
|
8
8
|
def initialize_distribution mu_init: 0, sigma_init: 1
|
9
|
-
@mu = NMatrix.new([1, ndims], mu_init, dtype:
|
9
|
+
@mu = NMatrix.new([1, ndims], mu_init, dtype: dtype)
|
10
10
|
sigma_init = [sigma_init]*ndims unless sigma_init.kind_of? Enumerable
|
11
|
-
@variances = NMatrix.new([1,ndims], sigma_init, dtype:
|
12
|
-
@sigma = NMatrix.diagonal(variances, dtype:
|
11
|
+
@variances = NMatrix.new([1,ndims], sigma_init, dtype: dtype)
|
12
|
+
@sigma = NMatrix.diagonal(variances, dtype: dtype)
|
13
13
|
end
|
14
14
|
|
15
15
|
def train picks: sorted_inds
|
@@ -17,7 +17,7 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
17
17
|
g_sigma = utils.dot(picks**2 - 1)
|
18
18
|
@mu += sigma.dot(g_mu.transpose).transpose * lrate
|
19
19
|
@variances *= (g_sigma * lrate / 2).exponential
|
20
|
-
@sigma = NMatrix.diagonal(variances, dtype:
|
20
|
+
@sigma = NMatrix.diagonal(variances, dtype: dtype)
|
21
21
|
end
|
22
22
|
|
23
23
|
# Estimate algorithm convergence as total variance
|
@@ -32,9 +32,9 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
32
32
|
def load data
|
33
33
|
raise ArgumentError unless data.size == 2
|
34
34
|
mu_ary, variances_ary = data
|
35
|
-
@mu = NMatrix[*mu_ary, dtype:
|
36
|
-
@variances = NMatrix[*variances_ary, dtype:
|
37
|
-
@sigma = NMatrix.diagonal(variances, dtype:
|
35
|
+
@mu = NMatrix[*mu_ary, dtype: dtype]
|
36
|
+
@variances = NMatrix[*variances_ary, dtype: dtype]
|
37
|
+
@sigma = NMatrix.diagonal(variances, dtype: dtype)
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
@@ -5,12 +5,12 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
5
5
|
attr_reader :log_sigma
|
6
6
|
|
7
7
|
def initialize_distribution mu_init: 0, sigma_init: 1
|
8
|
-
@mu = NMatrix.new([1, ndims], mu_init, dtype:
|
8
|
+
@mu = NMatrix.new([1, ndims], mu_init, dtype: dtype)
|
9
9
|
sigma_init = [sigma_init]*ndims unless sigma_init.kind_of? Enumerable
|
10
|
-
@sigma = NMatrix.diag(sigma_init, dtype:
|
10
|
+
@sigma = NMatrix.diag(sigma_init, dtype: dtype)
|
11
11
|
# Works with the log of sigma to avoid continuous decompositions (thanks Sun Yi)
|
12
12
|
log_sigma_init = sigma_init.map &Math.method(:log)
|
13
|
-
@log_sigma = NMatrix.diag(log_sigma_init, dtype:
|
13
|
+
@log_sigma = NMatrix.diag(log_sigma_init, dtype: dtype)
|
14
14
|
end
|
15
15
|
|
16
16
|
def train picks: sorted_inds
|
@@ -38,8 +38,8 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
38
38
|
def load data
|
39
39
|
raise ArgumentError unless data.size == 2
|
40
40
|
mu_ary, log_sigma_ary = data
|
41
|
-
@mu = NMatrix[*mu_ary, dtype:
|
42
|
-
@log_sigma = NMatrix[*log_sigma_ary, dtype:
|
41
|
+
@mu = NMatrix[*mu_ary, dtype: dtype]
|
42
|
+
@log_sigma = NMatrix[*log_sigma_ary, dtype: dtype]
|
43
43
|
@sigma = log_sigma.exponential
|
44
44
|
end
|
45
45
|
end
|