machine_learning_workbench 0.2.1 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -6
- data/lib/machine_learning_workbench/compressor.rb +1 -0
- data/lib/machine_learning_workbench/compressor/online_vector_quantization.rb +8 -6
- data/lib/machine_learning_workbench/compressor/vector_quantization.rb +25 -7
- data/lib/machine_learning_workbench/monkey.rb +32 -1
- data/lib/machine_learning_workbench/neural_network/base.rb +11 -3
- data/lib/machine_learning_workbench/optimizer.rb +3 -0
- data/lib/machine_learning_workbench/optimizer/natural_evolution_strategies/fnes.rb +11 -0
- data/lib/machine_learning_workbench/optimizer/natural_evolution_strategies/rnes.rb +38 -0
- data/lib/machine_learning_workbench/tools/execution.rb +8 -3
- data/lib/machine_learning_workbench/tools/imaging.rb +5 -4
- data/lib/machine_learning_workbench/tools/verification.rb +9 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a0550319ef523cd49f7c09b635a4e21508cf730
|
4
|
+
data.tar.gz: b3fb9a716bfac1850bc5af0c8abf96f17f0292b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51b05034a2fffcc135388c5760b14067728d4fcf7210ff47d4a7f58f66fb174d3940fd538e3551a78d9df24a0c14b01e0f277aba2ceb898d7206302f8c30721b
|
7
|
+
data.tar.gz: 0dd1cf85fdb8577278882fe197e90032b1061a11fec9176b8d64ef38b97140876059ec964199007f52840eaab13ca2a25b2b01b8d0935c5f55c2ffc518f59124
|
data/README.md
CHANGED
@@ -43,7 +43,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
43
43
|
|
44
44
|
## Contributing
|
45
45
|
|
46
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
46
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/giuse/machine_learning_workbench.
|
47
47
|
|
48
48
|
## License
|
49
49
|
|
@@ -53,9 +53,9 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
53
53
|
|
54
54
|
Please feel free to contribute to this list (see `Contributing` above).
|
55
55
|
|
56
|
-
- NES stands for Natural Evolution Strategies. Check its [Wikipedia page](https://en.wikipedia.org/wiki/Natural_evolution_strategy) for more info.
|
57
|
-
- CMA-ES stands for Covariance Matrix Adaptation Evolution Strategy. Check its [Wikipedia page](https://en.wikipedia.org/wiki/CMA-ES) for more info.
|
58
|
-
- UL-ELR stands for Unsupervised Learning plus Evolutionary Reinforcement Learning, from the paper _"Intrinsically Motivated Neuroevolution for Vision-Based Reinforcement Learning" (ICDL2011)_. Check [here](https://exascale.info/members/giuseppe-cuccu/) for citation reference and pdf.
|
59
|
-
- BD-NES stands for Block Diagonal Natural Evolution
|
56
|
+
- **NES** stands for Natural Evolution Strategies. Check its [Wikipedia page](https://en.wikipedia.org/wiki/Natural_evolution_strategy) for more info.
|
57
|
+
- **CMA-ES** stands for Covariance Matrix Adaptation Evolution Strategy. Check its [Wikipedia page](https://en.wikipedia.org/wiki/CMA-ES) for more info.
|
58
|
+
- **UL-ELR** stands for Unsupervised Learning plus Evolutionary Reinforcement Learning, from the paper _"Intrinsically Motivated Neuroevolution for Vision-Based Reinforcement Learning" (ICDL2011)_. Check [here](https://exascale.info/members/giuseppe-cuccu/) for citation reference and pdf.
|
59
|
+
- **BD-NES** stands for Block Diagonal Natural Evolution Strategy, from the homonymous paper _"Block Diagonal Natural Evolution Strategies" (PPSN2012)_. Check [here](https://exascale.info/members/giuseppe-cuccu/) for citation reference and pdf.
|
60
|
+
- **RNES** stands for Radial Natural Evolution Strategy, from the paper _"Novelty-Based Restarts for Evolution Strategies" (CEC2011)_. Check [here](https://exascale.info/members/giuseppe-cuccu/) for citation reference and pdf.
|
60
61
|
- **Online VQ** stands for Online Vector Quantization, from the paper _"Intrinsically Motivated Neuroevolution for Vision-Based Reinforcement Learning" (ICDL2011)_. Check [here](https://exascale.info/members/giuseppe-cuccu/) for citation reference and pdf.
|
61
|
-
|
@@ -3,25 +3,27 @@ module MachineLearningWorkbench::Compressor
|
|
3
3
|
# Optimized for online training.
|
4
4
|
class OnlineVectorQuantization < VectorQuantization
|
5
5
|
|
6
|
-
attr_reader :min_lrate
|
6
|
+
attr_reader :min_lrate
|
7
7
|
|
8
8
|
def initialize min_lrate: 0.01, **opts
|
9
9
|
super **opts.merge({lrate: nil})
|
10
10
|
@min_lrate = min_lrate
|
11
|
-
@ntrains = [0]*ncentrs
|
12
11
|
end
|
13
12
|
|
13
|
+
# Overloading lrate check from original VQ
|
14
|
+
def check_lrate lrate; nil; end
|
15
|
+
|
14
16
|
# Decaying per-centroid learning rate.
|
15
17
|
# @param centr_idx [Integer] index of the centroid
|
16
18
|
# @param lower_bound [Float] minimum learning rate
|
19
|
+
# @note nicely overloads the `attr_reader` of parent class
|
17
20
|
def lrate centr_idx, lower_bound: min_lrate
|
18
21
|
[1/ntrains[centr_idx], lower_bound].max
|
19
22
|
end
|
20
23
|
|
21
|
-
|
22
|
-
|
23
|
-
def train_one *args, **opts
|
24
|
-
super.tap { |trg_idx| ntrains[trg_idx] += 1 }
|
24
|
+
def train_one *args, **kwargs
|
25
|
+
raise NotImplementedError, "Remember to overload this using the new lrate(idx)"
|
25
26
|
end
|
27
|
+
|
26
28
|
end
|
27
29
|
end
|
@@ -2,7 +2,7 @@ module MachineLearningWorkbench::Compressor
|
|
2
2
|
|
3
3
|
# Standard Vector Quantization
|
4
4
|
class VectorQuantization
|
5
|
-
attr_reader :ncentrs, :centrs, :dims, :vrange, :dtype, :lrate, :rng
|
5
|
+
attr_reader :ncentrs, :centrs, :dims, :vrange, :dtype, :lrate, :rng, :ntrains
|
6
6
|
Verification = MachineLearningWorkbench::Tools::Verification
|
7
7
|
|
8
8
|
def initialize ncentrs:, dims:, vrange:, dtype:, lrate:, rseed: Random.new_seed
|
@@ -10,15 +10,24 @@ module MachineLearningWorkbench::Compressor
|
|
10
10
|
@ncentrs = ncentrs
|
11
11
|
@dtype = dtype
|
12
12
|
@dims = Array(dims)
|
13
|
+
check_lrate lrate # hack: so that we can overload it in online_vq
|
13
14
|
@lrate = lrate
|
14
15
|
@vrange = case vrange
|
15
16
|
when Array
|
16
17
|
raise ArgumentError, "vrange size not 2: #{vrange}" unless vrange.size == 2
|
17
18
|
vrange.map &method(:Float)
|
18
|
-
when Range
|
19
|
+
when Range
|
20
|
+
[vrange.first, vrange.last].map &method(:Float)
|
19
21
|
else raise ArgumentError, "vrange: unrecognized type: #{vrange.class}"
|
20
22
|
end
|
21
23
|
@centrs = ncentrs.times.map { new_centr }
|
24
|
+
@ntrains = [0]*ncentrs # useful to understand what happens
|
25
|
+
end
|
26
|
+
|
27
|
+
# Verify lrate to be present and withing unit bounds
|
28
|
+
# As a separate method only so it can be overloaded in online_vq
|
29
|
+
def check_lrate lrate
|
30
|
+
raise ArgumentError, "Pass a `lrate` between 0 and 1" unless lrate&.between?(0,1)
|
22
31
|
end
|
23
32
|
|
24
33
|
# Creates a new (random) centroid
|
@@ -66,6 +75,8 @@ module MachineLearningWorkbench::Compressor
|
|
66
75
|
end
|
67
76
|
|
68
77
|
# Returns index and similitude of most similar centroid to vector
|
78
|
+
# @return [Array<Integer, Float>] the index of the most similar centroid,
|
79
|
+
# followed by the corresponding similarity
|
69
80
|
def most_similar_centr vec
|
70
81
|
simils = similarities vec
|
71
82
|
max_simil = simils.max
|
@@ -74,17 +85,20 @@ module MachineLearningWorkbench::Compressor
|
|
74
85
|
end
|
75
86
|
|
76
87
|
# Per-pixel errors in reconstructing vector
|
88
|
+
# @return [NMatrix] residuals
|
77
89
|
def reconstr_error vec
|
78
90
|
reconstruction(vec) - vec
|
79
91
|
end
|
80
92
|
|
81
93
|
# Train on one vector
|
82
|
-
# @param vec [NMatrix]
|
83
94
|
# @return [Integer] index of trained centroid
|
84
|
-
def train_one vec
|
85
|
-
|
95
|
+
def train_one vec
|
96
|
+
|
97
|
+
trg_idx, _simil = most_similar_centr(vec)
|
98
|
+
# note: uhm that actually looks like a dot product... optimizable?
|
99
|
+
# `[c[i], vec].dot([1-lrate, lrate])`
|
86
100
|
centrs[trg_idx] = centrs[trg_idx] * (1-lrate) + vec * lrate
|
87
|
-
Verification.in_range! centrs[trg_idx], vrange
|
101
|
+
# Verification.in_range! centrs[trg_idx], vrange # I verified it's not needed
|
88
102
|
trg_idx
|
89
103
|
end
|
90
104
|
|
@@ -94,7 +108,11 @@ module MachineLearningWorkbench::Compressor
|
|
94
108
|
# - Batch: canonical, centrs updated with each vec
|
95
109
|
# - Parallel: could be parallel either on simils or on training (?)
|
96
110
|
# Unsure on the correctness of either Parallel, let's stick with Batch
|
97
|
-
vec_lst.
|
111
|
+
vec_lst.each_with_index do |vec, i|
|
112
|
+
trained_idx = train_one vec
|
113
|
+
print '.' if debug
|
114
|
+
ntrains[trained_idx] += 1
|
115
|
+
end
|
98
116
|
end
|
99
117
|
end
|
100
118
|
end
|
@@ -145,9 +145,29 @@ module MachineLearningWorkbench::Monkey
|
|
145
145
|
end
|
146
146
|
|
147
147
|
|
148
|
+
# The NMatrix documentation refers to a function `#nrm2` (aliased to `#norm2`)
|
149
|
+
# to compute the norm of a matrix. Fun fact: that is the implementation for vectors,
|
150
|
+
# and calling it on a matrix returns NotImplementedError :) you have to toggle the
|
151
|
+
# source to understand why:
|
152
|
+
# http://sciruby.com/nmatrix/docs/NMatrix.html#method-i-norm2 .
|
153
|
+
# A search for the actual source on GitHub reveals a (I guess new?) method
|
154
|
+
# `#matrix_norm`, with a decent choice of norms to choose from. Unfortunately, as the
|
155
|
+
# name says, it is stuck to compute full-matrix norms.
|
156
|
+
# So I resigned to dance to `Array`s and back, and implemented it with `#each_rank`.
|
157
|
+
# Unexplicably, I get a list of constant values as the return value; same with
|
158
|
+
# `#each_row`.
|
159
|
+
# What can I say, we're back to referencing rows by index. I am just wasting too much
|
160
|
+
# time figuring out these details to write a generalized version with an optional
|
161
|
+
# `dimension` to go along.
|
162
|
+
# @return [NMatrix] the vector norm along the rows
|
163
|
+
def row_norms
|
164
|
+
norms = rows.times.map { |i| row(i).norm2 }
|
165
|
+
NMatrix.new [rows, 1], norms, dtype: dtype
|
166
|
+
end
|
167
|
+
|
148
168
|
# `NMatrix#to_a` has inconsistent behavior: single-row matrices are
|
149
169
|
# converted to one-dimensional Arrays rather than a 2D Array with
|
150
|
-
# only one row. Patching `#to_a` directly is not feasible as the
|
170
|
+
# only one row. Patching `#to_a` directly is not feasible as the
|
151
171
|
# constructor seems to depend on it, and I have little interest in
|
152
172
|
# investigating further.
|
153
173
|
# @return [Array<Array>] a consistent array representation, such that
|
@@ -187,6 +207,16 @@ module MachineLearningWorkbench::Monkey
|
|
187
207
|
end
|
188
208
|
end
|
189
209
|
end
|
210
|
+
|
211
|
+
module CPtrDumpable
|
212
|
+
def marshall_dump
|
213
|
+
[shape, dtype, data_pointer]
|
214
|
+
end
|
215
|
+
|
216
|
+
def marshall_load
|
217
|
+
raise NotImplementedError, "There's no setter for the data pointer!"
|
218
|
+
end
|
219
|
+
end
|
190
220
|
end
|
191
221
|
|
192
222
|
Array.include MachineLearningWorkbench::Monkey::Dimensionable
|
@@ -195,3 +225,4 @@ require 'nmatrix/lapack_plugin' # loads whichever is installed between atlas and
|
|
195
225
|
NMatrix.include MachineLearningWorkbench::Monkey::AdvancelyOperationable
|
196
226
|
Numeric.include MachineLearningWorkbench::Monkey::NumericallyApproximatable
|
197
227
|
NMatrix.include MachineLearningWorkbench::Monkey::MatrixApproximatable
|
228
|
+
NMatrix.include MachineLearningWorkbench::Monkey::CPtrDumpable
|
@@ -119,15 +119,18 @@ module MachineLearningWorkbench::NeuralNetwork
|
|
119
119
|
end
|
120
120
|
|
121
121
|
# Loads a plain list of weights into the weight matrices (one per layer).
|
122
|
-
# Preserves order.
|
122
|
+
# Preserves order. Reuses allocated memory if available.
|
123
123
|
# @input weights [Array<Float>] weights to load
|
124
124
|
# @return [true] always true. If something's wrong it simply fails, and if
|
125
125
|
# all goes well there's nothing to return but a confirmation to the caller.
|
126
126
|
def load_weights weights
|
127
127
|
raise ArgumentError unless weights.size == nweights
|
128
128
|
weights_iter = weights.each
|
129
|
-
@layers
|
130
|
-
|
129
|
+
@layers ||= layer_shapes.collect { |shape| NMatrix.new shape, dtype: dtype }
|
130
|
+
layers.each do |nmat|
|
131
|
+
nmat.each_with_indices do |_val, *idxs|
|
132
|
+
nmat[*idxs] = weights_iter.next
|
133
|
+
end
|
131
134
|
end
|
132
135
|
reset_state
|
133
136
|
return true
|
@@ -200,6 +203,11 @@ module MachineLearningWorkbench::NeuralNetwork
|
|
200
203
|
lambda { |x| 1.7159 * Math.tanh(2.0*x/3.0) + 1e-3*x }
|
201
204
|
end
|
202
205
|
|
206
|
+
# Rectified Linear Unit (ReLU)
|
207
|
+
def relu
|
208
|
+
lambda { |x| x>0 && x || 0 }
|
209
|
+
end
|
210
|
+
|
203
211
|
|
204
212
|
# @!method interface_methods
|
205
213
|
# Declaring interface methods - implement in child class!
|
@@ -4,4 +4,7 @@ end
|
|
4
4
|
require_relative 'optimizer/natural_evolution_strategies/base'
|
5
5
|
require_relative 'optimizer/natural_evolution_strategies/xnes'
|
6
6
|
require_relative 'optimizer/natural_evolution_strategies/snes'
|
7
|
+
require_relative 'optimizer/natural_evolution_strategies/rnes'
|
8
|
+
# FIX SPECS FIRST
|
9
|
+
# require_relative 'optimizer/natural_evolution_strategies/fnes'
|
7
10
|
require_relative 'optimizer/natural_evolution_strategies/bdnes'
|
@@ -0,0 +1,11 @@
|
|
1
|
+
|
2
|
+
module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
3
|
+
# Fixed Variance Natural Evolution Strategies
|
4
|
+
class FNES < RNES
|
5
|
+
|
6
|
+
def train picks: sorted_inds
|
7
|
+
g_mu = utils.dot(picks)
|
8
|
+
@mu += sigma.dot(g_mu.transpose).transpose * lrate
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
3
|
+
# Radial Natural Evolution Strategies
|
4
|
+
class RNES < Base
|
5
|
+
attr_reader :variance
|
6
|
+
|
7
|
+
def initialize_distribution mu_init: 0, sigma_init: 1
|
8
|
+
@mu = NMatrix.new([1, ndims], mu_init, dtype: dtype)
|
9
|
+
raise ArgumentError unless sigma_init.kind_of? Numeric
|
10
|
+
@variance = sigma_init
|
11
|
+
@sigma = id * variance
|
12
|
+
end
|
13
|
+
|
14
|
+
def train picks: sorted_inds
|
15
|
+
g_mu = utils.dot(picks)
|
16
|
+
g_sigma = utils.dot(picks.row_norms**2 - ndims).first # back to scalar
|
17
|
+
@mu += sigma.dot(g_mu.transpose).transpose * lrate
|
18
|
+
@variance *= Math.exp(g_sigma * lrate / 2)
|
19
|
+
@sigma = id * variance
|
20
|
+
end
|
21
|
+
|
22
|
+
# Estimate algorithm convergence based on variance
|
23
|
+
def convergence
|
24
|
+
variance
|
25
|
+
end
|
26
|
+
|
27
|
+
def save
|
28
|
+
[mu.to_consistent_a, variance]
|
29
|
+
end
|
30
|
+
|
31
|
+
def load data
|
32
|
+
raise ArgumentError unless data.size == 2
|
33
|
+
mu_ary, @variance = data
|
34
|
+
@mu = NMatrix[*mu_ary, dtype: dtype]
|
35
|
+
@sigma = id * variance
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -2,7 +2,9 @@ module MachineLearningWorkbench::Tools
|
|
2
2
|
module Execution
|
3
3
|
$fork_pids ||= []
|
4
4
|
|
5
|
-
#
|
5
|
+
# Executes block in a (detached) fork, saving the `pid` for later termination.
|
6
|
+
# @note add `ensure MachineLearningWorkbench::Tools.kill_forks` to the block
|
7
|
+
# where `in_fork` is called (see `#kill_forks`).
|
6
8
|
def self.in_fork &block
|
7
9
|
raise ArgumentError "Need block to be executed in fork" unless block
|
8
10
|
pid = fork(&block)
|
@@ -10,9 +12,12 @@ module MachineLearningWorkbench::Tools
|
|
10
12
|
$fork_pids << pid
|
11
13
|
end
|
12
14
|
|
13
|
-
#
|
15
|
+
# Kills processes spawned by `#in_fork`.
|
16
|
+
# Call this in an `ensure` block after using `in_fork`.
|
17
|
+
# => `ensure MachineLearningWorkbench::Tools.kill_forks`
|
14
18
|
def self.kill_forks
|
15
|
-
$fork_pids&.each { |pid| Process.kill
|
19
|
+
$fork_pids&.each { |pid| Process.kill('KILL', pid) rescue Errno::ESRCH }
|
20
|
+
$fork_pids = []
|
16
21
|
end
|
17
22
|
end
|
18
23
|
end
|
@@ -18,11 +18,12 @@ module MachineLearningWorkbench::Tools
|
|
18
18
|
end
|
19
19
|
|
20
20
|
# Show a NMatrix as image in a RMagick window
|
21
|
-
# @param disp_size the size of the image to display
|
22
|
-
# @param shape the true shape of the image (NMatrix could be flattened)
|
21
|
+
# @param disp_size [Array] the size of the image to display
|
22
|
+
# @param shape [Array] the true shape of the image (NMatrix could be flattened)
|
23
23
|
# @param in_fork [bool] whether to execute the display in fork (and continue running)
|
24
|
-
def self.display nmat, disp_size:
|
25
|
-
img = nmat_to_img
|
24
|
+
def self.display nmat, disp_size: nil, shape: nil, in_fork: true
|
25
|
+
img = nmat_to_img nmat, shape: shape
|
26
|
+
img.resize!(*disp_size, Magick::TriangleFilter,0.51) if disp_size
|
26
27
|
if in_fork
|
27
28
|
MachineLearningWorkbench::Tools::Execution.in_fork { img.display }
|
28
29
|
else
|
@@ -1,6 +1,15 @@
|
|
1
1
|
module MachineLearningWorkbench::Tools
|
2
2
|
module Verification
|
3
3
|
def self.in_range! nmat, vrange
|
4
|
+
# Raise if values not in range
|
5
|
+
vmin, vmax = vrange.to_a
|
6
|
+
nmat.each_with_indices do |v, *idxs|
|
7
|
+
raise "Value not in range" unless v&.between? vmin, vmax
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Fix if values not in range
|
12
|
+
def self.in_range nmat, vrange
|
4
13
|
vmin, vmax = vrange.to_a
|
5
14
|
nmat.each_with_indices do |v, *idxs|
|
6
15
|
nmat[*idxs] = vmin if v < vmin
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: machine_learning_workbench
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: '0.3'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Giuseppe Cuccu
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-03-
|
11
|
+
date: 2018-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -185,6 +185,8 @@ files:
|
|
185
185
|
- lib/machine_learning_workbench/optimizer.rb
|
186
186
|
- lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb
|
187
187
|
- lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb
|
188
|
+
- lib/machine_learning_workbench/optimizer/natural_evolution_strategies/fnes.rb
|
189
|
+
- lib/machine_learning_workbench/optimizer/natural_evolution_strategies/rnes.rb
|
188
190
|
- lib/machine_learning_workbench/optimizer/natural_evolution_strategies/snes.rb
|
189
191
|
- lib/machine_learning_workbench/optimizer/natural_evolution_strategies/xnes.rb
|
190
192
|
- lib/machine_learning_workbench/systems.rb
|