machine_learning_workbench 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/machine_learning_workbench/compressor/copy_vq.rb +3 -1
- data/lib/machine_learning_workbench/compressor/decaying_learning_rate_vq.rb +5 -2
- data/lib/machine_learning_workbench/compressor/incr_dict_vq.rb +20 -13
- data/lib/machine_learning_workbench/compressor/vector_quantization.rb +186 -27
- data/lib/machine_learning_workbench/neural_network/base.rb +1 -0
- data/lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb +9 -3
- data/lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb +9 -1
- data/lib/machine_learning_workbench/optimizer/natural_evolution_strategies/snes.rb +2 -1
- data/lib/machine_learning_workbench/optimizer/natural_evolution_strategies/xnes.rb +5 -0
- data/lib/machine_learning_workbench/tools.rb +1 -0
- data/lib/machine_learning_workbench/tools/logging.rb +33 -0
- data/machine_learning_workbench.gemspec +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f580e6defa79dfb0c801b9e57c336b23fdea7e7
|
4
|
+
data.tar.gz: 0e4f17edef5773bdc78f38184028a916e14bb52b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4810984620a1e19717465bafde01c908b3803cf872e49a326b6ee63788692d40b87623e053bd7e4c0563067dfd46f1117c4aebb6aca723f75dd6fef76857642
|
7
|
+
data.tar.gz: 7b93ca2e63b4d9f69aeb77efc552b8b94aeb2c52576041a41e30a1dc0cb924e78019cd00b6b5edffa49bac7c14a1d9ade9c1c0f1c6bfab0bc0ae1bf014df4ee5
|
@@ -41,7 +41,9 @@ module MachineLearningWorkbench::Compressor
|
|
41
41
|
@next_train += 1
|
42
42
|
# require 'pry'; binding.pry if next_train == ncentrs
|
43
43
|
puts "Overwriting centr #{next_train}"
|
44
|
-
|
44
|
+
# norm_vec = vec / NLinalg.norm(vec)
|
45
|
+
# centrs[trg_idx, true] = norm_vec
|
46
|
+
centrs[trg_idx, true] = vec
|
45
47
|
trg_idx
|
46
48
|
end
|
47
49
|
|
@@ -29,9 +29,12 @@ module MachineLearningWorkbench::Compressor
|
|
29
29
|
|
30
30
|
# Train on one vector
|
31
31
|
# @return [Integer] index of trained centroid
|
32
|
-
def train_one vec
|
32
|
+
def train_one vec, eps: nil
|
33
|
+
# NOTE: ignores epsilon if passed
|
33
34
|
trg_idx, _simil = most_similar_centr(vec)
|
34
|
-
|
35
|
+
# norm_vec = vec / NLinalg.norm(vec)
|
36
|
+
# centrs[trg_idx, true] = centrs[trg_idx, true] * (1-lrate(trg_idx)) + norm_vec * lrate(trg_idx)
|
37
|
+
centrs[trg_idx, true] = centrs[trg_idx, true] * (1-lrate(trg_idx)) + vec * lrate(trg_idx)
|
35
38
|
trg_idx
|
36
39
|
end
|
37
40
|
|
@@ -11,12 +11,13 @@ module MachineLearningWorkbench::Compressor
|
|
11
11
|
|
12
12
|
def initialize **opts
|
13
13
|
puts "Ignoring learning rate: `lrate: #{opts[:lrate]}`" if opts[:lrate]
|
14
|
-
puts "Ignoring similarity: `simil_type: #{opts[:simil_type]}`"
|
14
|
+
puts "Ignoring similarity: `simil_type: #{opts[:simil_type]}`" unless opts[:simil_type] == :dot
|
15
15
|
puts "Ignoring ncentrs: `ncentrs: #{opts[:ncentrs]}`" if opts[:ncentrs]
|
16
16
|
# TODO: try different epsilons to reduce the number of states
|
17
17
|
# for example, in qbert we care what is lit and what is not, not the colors
|
18
18
|
@equal_simil = opts.delete(:equal_simil) || 0.0
|
19
|
-
super **opts.merge({ncentrs: 1, lrate: nil, simil_type:
|
19
|
+
super **opts.merge({ncentrs: 1, lrate: nil, simil_type: :dot})
|
20
|
+
|
20
21
|
@ntrains = nil # will disable the counting
|
21
22
|
end
|
22
23
|
|
@@ -28,17 +29,23 @@ module MachineLearningWorkbench::Compressor
|
|
28
29
|
# - create new centroid from the image
|
29
30
|
# @return [Integer] index of new centroid
|
30
31
|
def train_one vec, eps: equal_simil
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
32
|
+
# NOTE: novelty needs to be re-computed for each image, as after each
|
33
|
+
# training the novelty signal changes!
|
34
|
+
|
35
|
+
# NOTE the reconstruction error here depends once more on the _color_
|
36
|
+
# this is wrong and should be taken out of the equation
|
37
|
+
# NOTE: this is fixed if I use the differences sparse coding method
|
38
|
+
residual_img = reconstr_error(vec)
|
39
|
+
rec_err = residual_img.mean
|
40
|
+
return -1 if rec_err < eps
|
41
|
+
puts "Creating centr #{ncentrs} (rec_err: #{rec_err})"
|
42
|
+
# norm_vec = vec / NLinalg.norm(vec)
|
43
|
+
# @centrs = centrs.concatenate norm_vec
|
44
|
+
# @centrs = centrs.concatenate vec
|
45
|
+
@centrs = centrs.concatenate residual_img
|
46
|
+
# HACK: make it more general by using `code_size`
|
47
|
+
@utility = @utility.concatenate [0] * (encoding_type == :sparse_coding_v1 ? 2 : 1)
|
48
|
+
ncentrs
|
42
49
|
end
|
43
50
|
|
44
51
|
end
|
@@ -4,7 +4,7 @@ module MachineLearningWorkbench::Compressor
|
|
4
4
|
|
5
5
|
# Standard Vector Quantization
|
6
6
|
class VectorQuantization
|
7
|
-
attr_reader :
|
7
|
+
attr_reader :centrs, :dims, :vrange, :init_centr_vrange, :lrate,
|
8
8
|
:simil_type, :encoding_type, :rng, :ntrains, :utility, :ncodes
|
9
9
|
attr_writer :utility, :ncodes # allows access from outside
|
10
10
|
|
@@ -12,12 +12,11 @@ module MachineLearningWorkbench::Compressor
|
|
12
12
|
|
13
13
|
@rng = Random.new rseed # TODO: RNG CURRENTLY NOT USED!!
|
14
14
|
|
15
|
-
@ncentrs = ncentrs
|
16
15
|
@dims = Array(dims)
|
17
16
|
check_lrate lrate # hack: so that we can overload it in dlr_vq
|
18
17
|
@lrate = lrate
|
19
|
-
@simil_type = simil_type ||
|
20
|
-
@encoding_type = encoding_type ||
|
18
|
+
@simil_type = simil_type || raise("missing simil_type")
|
19
|
+
@encoding_type = encoding_type || raise("missing encoding_type")
|
21
20
|
@init_centr_vrange ||= vrange
|
22
21
|
@vrange = case vrange
|
23
22
|
when Array
|
@@ -27,12 +26,21 @@ module MachineLearningWorkbench::Compressor
|
|
27
26
|
[vrange.first, vrange.last].map &method(:Float)
|
28
27
|
else raise ArgumentError, "vrange: unrecognized type: #{vrange.class}"
|
29
28
|
end
|
30
|
-
init_centrs
|
31
|
-
@ntrains = [0]*ncentrs
|
32
|
-
@utility = NArray.zeros [
|
29
|
+
init_centrs nc: ncentrs
|
30
|
+
@ntrains = [0]*ncentrs # per-centroid number of trainings
|
31
|
+
@utility = NArray.zeros [code_size] # trace how 'useful' are centroids to encodings
|
33
32
|
@ncodes = 0
|
34
33
|
end
|
35
34
|
|
35
|
+
def ncentrs
|
36
|
+
@centrs.shape.first
|
37
|
+
end
|
38
|
+
|
39
|
+
# HACKKETY HACKKETY HACK (can't wait to refactor after the deadline)
|
40
|
+
def code_size
|
41
|
+
encoding_type == :sparse_coding_v1 ? 2*ncentrs : ncentrs
|
42
|
+
end
|
43
|
+
|
36
44
|
# Verify lrate to be present and withing unit bounds
|
37
45
|
# As a separate method only so it can be overloaded in `DecayingLearningRateVQ`
|
38
46
|
def check_lrate lrate
|
@@ -41,7 +49,7 @@ module MachineLearningWorkbench::Compressor
|
|
41
49
|
|
42
50
|
# Initializes a list of centroids
|
43
51
|
def init_centrs nc: ncentrs, base: nil, proport: nil
|
44
|
-
@centrs = nc.times.map { new_centr base, proport }
|
52
|
+
@centrs = nc.times.map { new_centr base, proport }.to_na
|
45
53
|
end
|
46
54
|
|
47
55
|
# Creates a new (random) centroid
|
@@ -56,39 +64,42 @@ module MachineLearningWorkbench::Compressor
|
|
56
64
|
ret
|
57
65
|
end
|
58
66
|
|
59
|
-
SIMIL = {
|
60
|
-
|
61
|
-
|
62
|
-
}
|
67
|
+
# SIMIL = {
|
68
|
+
# dot: -> (centr, vec) { centr.dot(vec) },
|
69
|
+
# mse: -> (centr, vec) { -((centr-vec)**2).sum / centr.size }
|
70
|
+
# }
|
63
71
|
|
64
72
|
# Computes similarities between vector and all centroids
|
65
73
|
def similarities vec, type: simil_type
|
66
74
|
raise NotImplementedError if vec.shape.size > 1
|
67
|
-
|
68
|
-
|
69
|
-
#
|
70
|
-
|
75
|
+
raise "need to check since centrs is a NArray now" if type == :mse
|
76
|
+
# simil_fn = SIMIL[type] || raise(ArgumentError, "Unrecognized simil #{type}")
|
77
|
+
# centrs.map { |centr| simil_fn.call centr, vec }
|
78
|
+
centrs.dot vec
|
71
79
|
end
|
72
80
|
|
73
81
|
# Encode a vector
|
74
82
|
# tracks utility of centroids based on how much they contribute to encoding
|
75
83
|
# TODO: `encode = Encodings.const_get(type)` in initialize`
|
76
84
|
# NOTE: hashes of lambdas or modules cannot access ncodes and utility
|
85
|
+
# TODO: refactor anyway through `stats` object, this thing is getting out of hand
|
77
86
|
def encode vec, type: encoding_type
|
78
|
-
simils = similarities vec
|
79
87
|
case type
|
80
88
|
when :most_similar
|
89
|
+
simils = similarities vec
|
81
90
|
code = simils.max_index
|
82
91
|
@ncodes += 1
|
83
92
|
@utility[code] += 1
|
84
93
|
code
|
85
94
|
when :most_similar_ary
|
95
|
+
simils = similarities vec
|
86
96
|
code = simils.new_zeros
|
87
97
|
code[simils.max_index] = 1
|
88
98
|
@ncodes += 1
|
89
99
|
@utility += code
|
90
100
|
code
|
91
101
|
when :ensemble
|
102
|
+
simils = similarities vec
|
92
103
|
code = simils
|
93
104
|
tot = simils.sum
|
94
105
|
tot = 1 if tot < 1e-5 # HACK: avoid division by zero
|
@@ -97,16 +108,140 @@ module MachineLearningWorkbench::Compressor
|
|
97
108
|
@utility += (contrib - utility) / ncodes # cumulative moving average
|
98
109
|
code
|
99
110
|
when :norm_ensemble
|
111
|
+
simils = similarities vec
|
100
112
|
tot = simils.sum
|
113
|
+
# NOTE this actually makes a big discontinuity if the total is equal to zero.
|
114
|
+
# Does that even ever happen? I guess only w/ reset img (zeros) as lone centroid.
|
115
|
+
# Which after first gen is really useless and should just be dropped anyway...
|
101
116
|
tot = 1 if tot < 1e-5 # HACK: avoid division by zero
|
102
117
|
code = simils / tot
|
118
|
+
@ncodes += 1
|
119
|
+
@utility += (code - utility) / ncodes # cumulative moving average
|
120
|
+
code
|
121
|
+
when :sparse_coding_v1
|
122
|
+
raise "requires centroids normalized to unit length!"
|
123
|
+
@encoder = nil if @encoder&.shape&.first != centrs.shape.first
|
124
|
+
# Danafar & Cuccu: compact form linear regression encoder
|
125
|
+
@encoder ||= (centrs.dot centrs.transpose).invert.dot centrs
|
126
|
+
|
127
|
+
raw_code = @encoder.dot(vec)
|
128
|
+
# separate positive and negative features (NOTE: all features will be positive)
|
129
|
+
# i.e. split[0...n] = max {0, raw[i]}; split[n...2*n] = max {0, -raw[i]}
|
130
|
+
# TODO: cite Coates & Ng
|
131
|
+
# TODO: optimize and remove redundant variables
|
132
|
+
split_code = raw_code.concatenate(-raw_code)
|
133
|
+
split_code[split_code<0] = 0
|
134
|
+
# normalize such that the code sums to 1
|
135
|
+
norm_code = split_code / split_code.sum
|
136
|
+
# Danafar: drop to say 80% of info (à la pca)
|
137
|
+
thold = 0.2
|
138
|
+
sparse_code = norm_code.dup
|
139
|
+
sum = 0
|
140
|
+
# NOTE: the last element in the sort below has the highest contribution and
|
141
|
+
# should NEVER be put to 0, even if it could contribute alone to 100% of the
|
142
|
+
# total
|
143
|
+
# NOTE: upon further study I disagree this represent information content unless
|
144
|
+
# the centroids are unit vectors. So I'm commenting this implementation now,
|
145
|
+
# together with the following, until I implement a switch to normalize the
|
146
|
+
# centroids based on configuration.
|
147
|
+
|
148
|
+
|
149
|
+
|
150
|
+
# BUG IN NARRAY SORT!! ruby-numo/numo-narray#97
|
151
|
+
# norm_code.sort_index[0...-1].each do |idx|
|
152
|
+
norm_code.size.times.sort_by { |i| norm_code[i] }[0...-1].each do |idx|
|
153
|
+
|
154
|
+
|
155
|
+
|
156
|
+
sparse_code[idx] = 0
|
157
|
+
sum += norm_code[idx]
|
158
|
+
break if sum >= thold # we know the code's total is normalized to 1 and has no negatives
|
159
|
+
end
|
160
|
+
code = sparse_code / sparse_code.sum # re-normalize sum to 1
|
161
|
+
|
162
|
+
@ncodes += 1
|
163
|
+
@utility += (code - utility) / ncodes # cumulative moving average
|
164
|
+
code
|
165
|
+
when :sparse_coding_v2
|
166
|
+
# Cuccu & Danafar: incremental reconstruction encoding
|
167
|
+
# turns out to be closely related to (Orthogonal) Matching Pursuit
|
168
|
+
raise "requires centroids normalized to unit length!"
|
169
|
+
# return centrs.dot vec # speed test for the rest of the system
|
170
|
+
sparse_code = NArray.zeros code_size
|
171
|
+
resid = vec
|
172
|
+
# cap the number of non-zero elements in the code
|
173
|
+
max_nonzero = [1,ncentrs/3].max
|
174
|
+
max_nonzero.times do |i|
|
175
|
+
# OPT: remove msc from centrs at each loop
|
176
|
+
# the algorithm should work even without this opt because
|
177
|
+
# we are working on the residuals each time
|
178
|
+
simils = centrs.dot resid
|
179
|
+
|
180
|
+
|
181
|
+
|
182
|
+
# BUG IN NARRAY SORT!! ruby-numo/numo-narray#97
|
183
|
+
# msc = simils.max_index
|
184
|
+
simils = simils.to_a
|
185
|
+
simils_abs = simils.map &:abs
|
186
|
+
msc = simils_abs.index simils_abs.max # most similar centroid
|
187
|
+
|
188
|
+
|
189
|
+
|
190
|
+
max_simil = simils[msc]
|
191
|
+
# remember to distinguish here to use the pos/neg features trick
|
192
|
+
sparse_code[msc] = max_simil
|
193
|
+
reconstr = max_simil * centrs[msc, true]
|
194
|
+
resid -= reconstr
|
195
|
+
# puts "resid#{i} #{resid.abs.mean}" # if debug
|
196
|
+
epsilon = 0.005
|
197
|
+
# print resid.abs.mean, ' '
|
198
|
+
# print sparse_code.to_a, ' '
|
199
|
+
break if resid.abs.mean <= epsilon
|
200
|
+
end
|
201
|
+
|
202
|
+
# should normalize sum to 1?
|
203
|
+
code = sparse_code #/ sparse_code.sum # normalize sum to 1
|
204
|
+
|
103
205
|
@ncodes += 1
|
104
206
|
@utility += (code - utility) / ncodes # cumulative moving average
|
105
207
|
code
|
106
208
|
when :sparse_coding
|
107
|
-
|
209
|
+
# Cuccu: Direct residual encoding
|
210
|
+
# return centrs.dot vec # speed test for the rest of the system
|
211
|
+
sparse_code = NArray.zeros code_size
|
212
|
+
resid = vec
|
213
|
+
# cap the number of non-zero elements in the code
|
214
|
+
max_nonzero = [1,ncentrs/3].max
|
215
|
+
max_nonzero.times do |i|
|
216
|
+
# OPT: remove msc from centrs at each loop
|
217
|
+
# the algorithm should work even without this opt because
|
218
|
+
# we are working on the residuals each time
|
219
|
+
diff = (centrs - resid).abs.sum(1)
|
220
|
+
|
108
221
|
|
109
222
|
|
223
|
+
# BUG IN NARRAY SORT!! ruby-numo/numo-narray#97
|
224
|
+
# msc = diff.max_index
|
225
|
+
diff = diff.to_a
|
226
|
+
msc = diff.index diff.min # most similar centroid
|
227
|
+
|
228
|
+
|
229
|
+
|
230
|
+
min_diff = diff[msc]
|
231
|
+
# remember to distinguish here to use the pos/neg features trick
|
232
|
+
sparse_code[msc] = 1
|
233
|
+
reconstr = centrs[msc, true]
|
234
|
+
resid -= reconstr
|
235
|
+
resid[(resid<0).where] = 0 # ignore artifacts introduced by the centroids in reconstruction
|
236
|
+
|
237
|
+
# puts "resid#{i} #{resid.abs.mean}" # if debug
|
238
|
+
epsilon = 0.005
|
239
|
+
# print resid.abs.mean, ' ' if $ngen == 2; exit if $ngen==3
|
240
|
+
# print sparse_code.to_a, ' ' if $ngen == 3; exit if $ngen==4
|
241
|
+
break if resid.abs.mean <= epsilon
|
242
|
+
end
|
243
|
+
|
244
|
+
code = sparse_code
|
110
245
|
@ncodes += 1
|
111
246
|
@utility += (code - utility) / ncodes # cumulative moving average
|
112
247
|
code
|
@@ -118,16 +253,33 @@ module MachineLearningWorkbench::Compressor
|
|
118
253
|
def reconstruction code, type: encoding_type
|
119
254
|
case type
|
120
255
|
when :most_similar
|
121
|
-
centrs[code]
|
256
|
+
centrs[code, true]
|
122
257
|
when :most_similar_ary
|
123
|
-
centrs[code.eq(1)
|
258
|
+
centrs[code.eq(1), true]
|
124
259
|
when :ensemble
|
125
|
-
tot = code.reduce :+
|
126
|
-
centrs.zip(code).map { |centr, contr| centr*contr/tot }.reduce :+
|
260
|
+
# tot = code.reduce :+
|
261
|
+
# centrs.zip(code).map { |centr, contr| centr*contr/tot }.reduce :+
|
262
|
+
centrs.dot(code) / code.sum
|
127
263
|
when :norm_ensemble
|
128
|
-
centrs.
|
264
|
+
centrs.dot code
|
265
|
+
# centrs.zip(code).map { |centr, contr| centr*contr }.reduce :+
|
266
|
+
when :sparse_coding_v1
|
267
|
+
raise "requires normalized centroids!"
|
268
|
+
reconstr_code = code[0...(code.size/2)] - code[(code.size/2)..-1]
|
269
|
+
reconstr = centrs.transpose.dot reconstr_code
|
270
|
+
when :sparse_coding_v2
|
271
|
+
raise "requires normalized centroids!"
|
272
|
+
|
273
|
+
|
274
|
+
# BUG IN NARRAY DOT!! ruby-numo/numo-narray#99
|
275
|
+
# reconstr = code.dot centrs
|
276
|
+
reconstr = code.expand_dims(0).dot centrs
|
277
|
+
|
278
|
+
|
129
279
|
when :sparse_coding
|
130
|
-
|
280
|
+
# the code is binary, so just sum over the corresponding centroids
|
281
|
+
# note: sum, not mean, because of how it's used in reconstr_error
|
282
|
+
reconstr = centrs[code.cast_to(Numo::Bit).where, true].sum(0)
|
131
283
|
else raise ArgumentError, "unrecognized reconstruction type: #{type}"
|
132
284
|
end
|
133
285
|
end
|
@@ -145,16 +297,23 @@ module MachineLearningWorkbench::Compressor
|
|
145
297
|
# @return [NArray] residuals
|
146
298
|
def reconstr_error vec, code: nil, type: encoding_type
|
147
299
|
code ||= encode vec, type: type
|
148
|
-
|
300
|
+
resid = vec - reconstruction(code, type: type)
|
301
|
+
# we ignore the extra stuff coming from the centroids,
|
302
|
+
# only care that everything in the obs is represented in centrs
|
303
|
+
resid[resid<0] = 0 if encoding_type == :sparse_coding
|
304
|
+
resid
|
149
305
|
end
|
150
306
|
|
151
307
|
# Train on one vector
|
152
308
|
# @return [Integer] index of trained centroid
|
153
|
-
def train_one vec
|
309
|
+
def train_one vec, eps: nil
|
310
|
+
# NOTE: ignores epsilon if passed
|
154
311
|
trg_idx, _simil = most_similar_centr(vec)
|
155
312
|
# note: uhm that actually looks like a dot product... maybe faster?
|
156
313
|
# `[c[i], vec].dot([1-lrate, lrate])`
|
157
|
-
|
314
|
+
# norm_vec = vec / NLinalg.norm(vec)
|
315
|
+
# centrs[trg_idx, true] = centrs[trg_idx, true] * (1-lrate) + norm_vec * lrate
|
316
|
+
centrs[trg_idx, true] = centrs[trg_idx, true] * (1-lrate) + vec * lrate
|
158
317
|
trg_idx
|
159
318
|
end
|
160
319
|
|
@@ -125,14 +125,20 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
125
125
|
# this_best = sorted.last.take(2)
|
126
126
|
# NArray[*sorted.map(&:last)]
|
127
127
|
|
128
|
-
|
128
|
+
|
129
|
+
|
130
|
+
# BUG IN NARRAY SORT!! ruby-numo/numo-narray#97
|
131
|
+
# sort_idxs = fits.sort_index
|
132
|
+
sort_idxs = fits.size.times.sort_by { |i| fits[i] }.to_na
|
133
|
+
|
134
|
+
|
135
|
+
|
129
136
|
sort_idxs = sort_idxs.reverse if opt_type == :min
|
130
137
|
this_best = [fits[sort_idxs[-1]], inds[sort_idxs[-1], true]]
|
131
|
-
|
132
138
|
opt_cmp_fn = opt_type==:min ? :< : :>
|
133
139
|
@best = this_best if this_best.first.send(opt_cmp_fn, best.first)
|
134
140
|
|
135
|
-
samples[sort_idxs,true]
|
141
|
+
samples[sort_idxs, true]
|
136
142
|
end
|
137
143
|
|
138
144
|
# @!method interface_methods
|
@@ -68,7 +68,15 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
68
68
|
# opt_cmp_fn = opt_type==:min ? :< : :>
|
69
69
|
# @best = this_best if this_best.first.send(opt_cmp_fn, best.first)
|
70
70
|
# sorted_samples = sorted.map(&:last)
|
71
|
-
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
# BUG IN NARRAY SORT!! ruby-numo/numo-narray#97
|
75
|
+
# sort_idxs = fits.sort_index
|
76
|
+
sort_idxs = fits.size.times.sort_by { |i| fits[i] }.to_na
|
77
|
+
|
78
|
+
|
79
|
+
|
72
80
|
sort_idxs = sort_idxs.reverse if opt_type == :min
|
73
81
|
this_best = [fits[sort_idxs[-1]], full_inds[sort_idxs[-1], true]]
|
74
82
|
opt_cmp_fn = opt_type==:min ? :< : :>
|
@@ -23,7 +23,8 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
23
23
|
when Numeric
|
24
24
|
NArray.new([ndims]).fill(sigma_init)
|
25
25
|
else
|
26
|
-
raise ArgumentError, "Something is wrong with sigma_init: #{sigma_init}"
|
26
|
+
raise ArgumentError, "Something is wrong with sigma_init: #{sigma_init}" \
|
27
|
+
"(did you remember to copy the other cases from XNES?)"
|
27
28
|
end
|
28
29
|
@sigma = @variances.diag
|
29
30
|
end
|
@@ -7,6 +7,11 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
|
|
7
7
|
|
8
8
|
def initialize_distribution mu_init: 0, sigma_init: 1
|
9
9
|
@mu = case mu_init
|
10
|
+
when Range # initialize with random in range
|
11
|
+
raise ArgumentError, "mu_init: `Range` start/end in `Float`s" \
|
12
|
+
unless mu_init.first.kind_of?(Float) && mu_init.last.kind_of?(Float)
|
13
|
+
mu_rng = Random.new rng.rand 10**Random.new_seed.size
|
14
|
+
NArray[*ndims.times.map { mu_rng.rand mu_init }]
|
10
15
|
when Array
|
11
16
|
raise ArgumentError unless mu_init.size == ndims
|
12
17
|
NArray[mu_init]
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MachineLearningWorkbench::Tools
|
4
|
+
module Logging
|
5
|
+
# Splits calls to standard streams to be both displayed on terminal and saved to file
|
6
|
+
class LogSplitter < File
|
7
|
+
def initialize dest
|
8
|
+
fname = if File.directory?(dest)
|
9
|
+
"#{dest}/#{Time.now.strftime "%y%m%d_%H%M"}.log"
|
10
|
+
else dest
|
11
|
+
end
|
12
|
+
super fname, 'w'
|
13
|
+
end
|
14
|
+
|
15
|
+
def write *args
|
16
|
+
STDOUT.write *args
|
17
|
+
super
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.split_to dest, also_stderr: false
|
22
|
+
$stdout = LogSplitter.new dest
|
23
|
+
$stderr = $stdout if also_stderr
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.restore_streams
|
27
|
+
logger = $stdout
|
28
|
+
$stdout = STDOUT
|
29
|
+
$stderr = STDERR
|
30
|
+
logger.close
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -48,6 +48,6 @@ Gem::Specification.new do |spec|
|
|
48
48
|
spec.requirements << "libopenblas-base" # requirement for `numo-linalg`
|
49
49
|
spec.requirements << "liblapacke" # requirement for `numo-linalg`
|
50
50
|
spec.add_dependency "numo-narray", "~> 0.9"
|
51
|
-
spec.add_dependency "numo-linalg", "~> 0.1
|
51
|
+
spec.add_dependency "numo-linalg", "~> 0.1"
|
52
52
|
spec.add_dependency "parallel", "~> 1.12"
|
53
53
|
end
|
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.8.0
|
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-
|
11
|
+
date: 2018-05-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -156,14 +156,14 @@ dependencies:
|
|
156
156
|
requirements:
|
157
157
|
- - "~>"
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version: 0.1
|
159
|
+
version: '0.1'
|
160
160
|
type: :runtime
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
|
-
version: 0.1
|
166
|
+
version: '0.1'
|
167
167
|
- !ruby/object:Gem::Dependency
|
168
168
|
name: parallel
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -223,6 +223,7 @@ files:
|
|
223
223
|
- lib/machine_learning_workbench/tools.rb
|
224
224
|
- lib/machine_learning_workbench/tools/execution.rb
|
225
225
|
- lib/machine_learning_workbench/tools/imaging.rb
|
226
|
+
- lib/machine_learning_workbench/tools/logging.rb
|
226
227
|
- lib/machine_learning_workbench/tools/normalization.rb
|
227
228
|
- lib/machine_learning_workbench/tools/verification.rb
|
228
229
|
- machine_learning_workbench.gemspec
|