machine_learning_workbench 0.7.0 → 0.8.0
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/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
|