Class: MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb

Overview

Natural Evolution Strategies base class

Direct Known Subclasses

BDNES, RNES, SNES, XNES

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(ndims, obj_fn, opt_type, rseed: nil, mu_init: 0, sigma_init: 1, parallel_fit: false, rescale_popsize: 1, rescale_lrate: 1) ⇒ Base

NES object initialization

Raises:

  • (ArgumentError)


22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 22

def initialize ndims, obj_fn, opt_type, rseed: nil, mu_init: 0, sigma_init: 1, parallel_fit: false, rescale_popsize: 1, rescale_lrate: 1
  raise ArgumentError unless [:min, :max].include? opt_type
  raise ArgumentError unless obj_fn.respond_to? :call
  @ndims, @opt_type, @obj_fn, @parallel_fit = ndims, opt_type, obj_fn, parallel_fit
  @rescale_popsize, @rescale_lrate = rescale_popsize, rescale_lrate
  @eye = NArray.eye(ndims)
  rseed ||= Random.new_seed
  # puts "NES rseed: #{s}"  # currently disabled
  @rng = Random.new rseed
  @best = [(opt_type==:max ? -1 : 1) * Float::INFINITY, nil]
  @last_fits = []
  initialize_distribution mu_init: mu_init, sigma_init: sigma_init
end

Instance Attribute Details

#bestObject (readonly)

Returns the value of attribute best.



5
6
7
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 5

def best
  @best
end

#eyeObject (readonly)

Returns the value of attribute eye.



5
6
7
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 5

def eye
  @eye
end

#last_fitsObject (readonly)

Returns the value of attribute last_fits.



5
6
7
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 5

def last_fits
  @last_fits
end

#muObject (readonly)

Returns the value of attribute mu.



5
6
7
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 5

def mu
  @mu
end

#ndimsObject (readonly)

Returns the value of attribute ndims.



5
6
7
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 5

def ndims
  @ndims
end

#obj_fnObject (readonly)

Returns the value of attribute obj_fn.



5
6
7
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 5

def obj_fn
  @obj_fn
end

#opt_typeObject (readonly)

Returns the value of attribute opt_type.



5
6
7
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 5

def opt_type
  @opt_type
end

#parallel_fitObject (readonly)

Returns the value of attribute parallel_fit.



5
6
7
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 5

def parallel_fit
  @parallel_fit
end

#rescale_lrateObject (readonly)

Returns the value of attribute rescale_lrate.



5
6
7
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 5

def rescale_lrate
  @rescale_lrate
end

#rescale_popsizeObject (readonly)

Returns the value of attribute rescale_popsize.



5
6
7
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 5

def rescale_popsize
  @rescale_popsize
end

#rngObject (readonly)

Returns the value of attribute rng.



5
6
7
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 5

def rng
  @rng
end

#sigmaObject (readonly)

Returns the value of attribute sigma.



5
6
7
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 5

def sigma
  @sigma
end

Instance Method Details

#cmaes_lrateNArray, Float

Magic numbers from CMA-ES (TODO: add proper citation)



70
71
72
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 70

def cmaes_lrate
  (3+Math.log(ndims)) / (5*Math.sqrt(ndims))
end

#cmaes_popsizeNArray, Integer

Magic numbers from CMA-ES (TODO: add proper citation)



76
77
78
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 76

def cmaes_popsize
  [5, 4 + (3*Math.log(ndims)).floor].max
end

#cmaes_utilitiesNArray

Magic numbers from CMA-ES (TODO: add proper citation)



56
57
58
59
60
61
62
63
64
65
66
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 56

def cmaes_utilities
  # Algorithm equations are meant for fitness maximization
  # Match utilities with individuals sorted by INCREASING fitness
  log_range = (1..popsize).collect do |v|
    [0, Math.log(popsize.to_f/2 - 1) - Math.log(v)].max
  end
  total = log_range.reduce(:+)
  buf = 1.0/popsize
  vals = log_range.collect { |v| v / total - buf }.reverse
  NArray[vals]
end

#interface_methodsObject

Declaring interface methods - implement these in child class!



133
134
135
136
137
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 133

[:train, :initialize_distribution, :convergence].each do |mname|
  define_method mname do
    raise NotImplementedError, "Implement in child class!"
  end
end

#lrateObject

Memoized automatic magic numbers NOTE: Doubling popsize and halving lrate often helps



52
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 52

def lrate;   @lrate     ||= cmaes_lrate * rescale_lrate end

#move_inds(inds) ⇒ NArray

Move standard normal samples to current distribution



92
93
94
95
96
97
98
99
100
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 92

def move_inds inds
  # TODO: can we reduce the transpositions?

  # multi_mu = NMatrix[*inds.rows.times.collect {mu.to_a}, dtype: dtype].transpose
  # (multi_mu + sigma.dot(inds.transpose)).transpose

  mu_tile = mu.tile(inds.shape.first, 1).transpose
  (mu_tile + sigma.dot(inds.transpose)).transpose
end

#popsizeObject

Memoized automatic magic numbers NOTE: Doubling popsize and halving lrate often helps



50
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 50

def popsize; @popsize   ||= cmaes_popsize * rescale_popsize end

#sorted_indsObject

Sorted individuals NOTE: Algorithm equations are meant for fitness maximization. Utilities need to be matched with individuals sorted by INCREASING fitness. Then reverse order for minimization.



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 106

def sorted_inds
  # Xumo::NArray implements the Box-Muller, but no random seed (yet)
  samples = standard_normal_samples
  # samples = NArray.new([popsize, ndims]).rand_norm(0,1)
  inds = move_inds(samples)
  fits = parallel_fit ? obj_fn.call(inds) : inds.map(&obj_fn)
  # Quick cure for NaN fitnesses
  fits.map { |x| x.nan? ? (opt_type==:max ? -1 : 1) * Float::INFINITY : x }
  @last_fits = fits # allows checking for stagnation

  # sorted = [fits.to_a, inds, samples.to_a].transpose.sort_by(&:first)
  # sorted.reverse! if opt_type==:min
  # this_best = sorted.last.take(2)
  # NArray[*sorted.map(&:last)]

  sort_idxs = fits.sort_index
  sort_idxs = sort_idxs.reverse if opt_type == :min
  this_best = [fits[sort_idxs[-1]], inds[sort_idxs[-1], true]]

  opt_cmp_fn = opt_type==:min ? :< : :>
  @best = this_best if this_best.first.send(opt_cmp_fn, best.first)

  samples[sort_idxs,true]
end

#standard_normal_sampleFloat

Note:

Xumo::NArray implements this but no random seed selection yet

Box-Muller transform: generates standard (unit) normal distribution samples



39
40
41
42
43
44
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 39

def standard_normal_sample
  rho = Math.sqrt(-2.0 * Math.log(rng.rand))
  theta = 2 * Math::PI * rng.rand
  tfn = rng.rand > 0.5 ? :cos : :sin
  rho * Math.send(tfn, theta)
end

#standard_normal_samplesNArray

Note:

Xumo::NArray implements this but no random seed selection yet

Samples a standard normal distribution to construct a NArray of

popsize multivariate samples of length ndims


84
85
86
87
88
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 84

def standard_normal_samples
  NArray.zeros([popsize, ndims]).tap do |ret|
    ret.each_with_index { |_,*i| ret[*i] = standard_normal_sample }
  end
end

#utilsObject

Memoized automatic magic numbers NOTE: Doubling popsize and halving lrate often helps



48
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/base.rb', line 48

def utils;   @utilities ||= cmaes_utilities end