Class: MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies::BDNES

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

Overview

Block-Diagonal Natural Evolution Strategies

Constant Summary collapse

MAX_RSEED =

same range as Random.new_seed

10**Random.new_seed.size

Instance Attribute Summary collapse

Attributes inherited from Base

#id, #ndims, #sigma

Instance Method Summary collapse

Methods inherited from Base

#cmaes_lrate, #cmaes_popsize, #cmaes_utilities, #interface_methods, #lrate, #move_inds, #sorted_inds, #standard_normal_sample, #standard_normal_samples, #utils

Constructor Details

#initialize(ndims_lst, obj_fn, opt_type, rseed: nil, **init_opts) ⇒ BDNES

initialize a list of XNES for each block



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb', line 12

def initialize ndims_lst, obj_fn, opt_type, rseed: nil, **init_opts
  # mu_init: 0, sigma_init: 1
  # init_opts = {rseed: rseed, mu_init: mu_init, sigma_init: sigma_init}
  # TODO: accept list of `mu_init`s and `sigma_init`s
  @ndims_lst, @obj_fn, @opt_type = ndims_lst, obj_fn, opt_type
  block_fit = -> (*args) { raise "Should never be called" }
  # the BD-NES seed should ensure deterministic reproducibility
  # but each block should have a different seed
  rseed ||= Random.new_seed
  # puts "BD-NES rseed: #{s}"  # currently disabled
  @rng = Random.new rseed
  @blocks = ndims_lst.map do |ndims|
    b_rseed = rng.rand MAX_RSEED
    XNES.new ndims, block_fit, opt_type, rseed: b_rseed, **init_opts
  end
  # Need `popsize` to be the same for all blocks, to make complete individuals
  @popsize = blocks.map(&:popsize).max
  blocks.each { |xnes| xnes.instance_variable_set :@popsize, popsize }

  @best = [(opt_type==:max ? -1 : 1) * Float::INFINITY, nil]
  @last_fits = []
end

Instance Attribute Details

#bestObject (readonly)

Returns the value of attribute best.



8
9
10
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb', line 8

def best
  @best
end

#blocksObject (readonly)

Returns the value of attribute blocks.



8
9
10
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb', line 8

def blocks
  @blocks
end

#last_fitsObject (readonly)

Returns the value of attribute last_fits.



8
9
10
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb', line 8

def last_fits
  @last_fits
end

#ndims_lstObject (readonly)

Returns the value of attribute ndims_lst.



8
9
10
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb', line 8

def ndims_lst
  @ndims_lst
end

#obj_fnObject (readonly)

Returns the value of attribute obj_fn.



8
9
10
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb', line 8

def obj_fn
  @obj_fn
end

#opt_typeObject (readonly)

Returns the value of attribute opt_type.



8
9
10
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb', line 8

def opt_type
  @opt_type
end

#popsizeObject (readonly)

Returns the value of attribute popsize.



8
9
10
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb', line 8

def popsize
  @popsize
end

#rngObject (readonly)

Returns the value of attribute rng.



8
9
10
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb', line 8

def rng
  @rng
end

Instance Method Details

#convergenceObject



85
86
87
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb', line 85

def convergence
  blocks.map(&:convergence).reduce(:+)
end

#load(data) ⇒ Object



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

def load data
  # raise "Hell!" unless data.size == 2
  fit = -> (*args) { raise "Should never be called" }
  @blocks = data.map do |block_data|
    ndims = block_data.first.size
    XNES.new(ndims, fit, opt_type).tap do |nes|
      nes.load block_data
    end
  end
end

#muObject



81
82
83
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb', line 81

def mu
  blocks.map(&:mu).reduce(&:hconcat)
end

#saveObject



89
90
91
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb', line 89

def save
  blocks.map &:save
end

#sorted_inds_lstObject



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb', line 35

def sorted_inds_lst
  # Build samples and inds from the list of blocks
  samples_lst, inds_lst = blocks.map do |xnes|
    samples = xnes.standard_normal_samples
    inds = xnes.move_inds(samples)
    [samples.to_a, inds]
  end.transpose

  # Join the individuals for evaluation
  full_inds = inds_lst.reduce(&:hconcat).to_a
  # Need to fix samples dimensions for sorting
  # - current dims: nblocks x ninds x [block sizes]
  # - for sorting: ninds x nblocks x [block sizes]
  full_samples = samples_lst.transpose

  # Evaluate fitness of complete individuals
  fits = obj_fn.call(full_inds)
  # 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

  # Sort inds based on fit and opt_type, save best
  sorted = [fits, full_inds, full_samples].transpose.sort_by(&:first)
  sorted.reverse! if opt_type==:min
  this_best = sorted.last.take(2)
  opt_cmp_fn = opt_type==:min ? :< : :>
  @best = this_best if this_best.first.send(opt_cmp_fn, best.first)
  sorted_samples = sorted.map(&:last)

  # Need to bring back sample dimensions for each block
  # - current dims: ninds x nblocks x [block sizes]
  # - target blocks list: nblocks x ninds x [block sizes]
  block_samples = sorted_samples.transpose

  # then back to NMatrix for usage in training
  block_samples.map { |sample| NMatrix[*sample, dtype: :float64] }
end

#train(picks: sorted_inds_lst) ⇒ Object

duck-type the interface: [:train, :mu, :convergence, :save, :load]



75
76
77
78
79
# File 'lib/machine_learning_workbench/optimizer/natural_evolution_strategies/bdnes.rb', line 75

def train picks: sorted_inds_lst
  blocks.zip(sorted_inds_lst).each do |xnes, s_inds|
    xnes.train picks: s_inds
  end
end