machine_learning_workbench 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: baafa2eb71e8bbcc83d6a320437c2a0ad8f5544e
4
- data.tar.gz: 5337ad91449767ca2754b41b2784212bfe5fbae7
3
+ metadata.gz: afdd4f1bf99c2abbe10f4c348531f4efabde3c73
4
+ data.tar.gz: 2334e9b7e5c4f276f94e75dd50164f8fa78cc699
5
5
  SHA512:
6
- metadata.gz: 276d67ea700d5d9a6c0ee450cf0839438c77d7b7ce8dce87173b13a0898d0615cf30cc1f803686cb2da9e76953e601f8e702948853b2332cdffd57f7250ed72f
7
- data.tar.gz: 3b9e2fdee7f0435d4acd52b902de928705e021f2e1715b58a09128d69cf129e4aaa7ad8f33d0545a74761d0b2345aa8df6a3ac112b7ebb8c67002431e686ce90
6
+ metadata.gz: 4f51ca6077627401bb1c27dfaed60cb84e5d70bdebe2f7c6d366abae299040e32ec7a4a1ad1ee3dc8ccc9aaa536a4125e91c2e97eaf043115d21e5e41d6a8e70
7
+ data.tar.gz: 884493c20fde5e8ac707f6c00b442d924a7dab5d783cf011926c2425b92116eea2bd1f10116a026b9b55db2210d918aa533eb0a6bc259b7bd0704df217b0985b
@@ -8,13 +8,13 @@ FFNN = WB::NeuralNetwork::FeedForward
8
8
 
9
9
  # Let's address the XOR problem, as it requires nonlinear fitting
10
10
  XOR = {[0,0] => 0, [1,0] => 1, [0,1] => 1, [1,1] => 0}
11
- # A classic [2,2,1] feed-forward network will do: 2 inputs, 2 hidden, 1 output
12
- # For other uses, make sure you match the first number to the number of inputs, and
13
- # the last one as the number of outputs; then add as many layers as needed, by
14
- # specifying the size of each. Here we have only one, of size 2.
15
- # NOTE: If this totals thousands of weights, you may want to switch to SNES or BDNES
16
- # for speed. In the second case, use the function `nweights_per_layer` when instantiating
17
- # BDNES rather than `nweights`.
11
+ # A classic [2,2,1] (2 inputs, 2 hidden neurons, 1 output neurons) feed-forward
12
+ # network with nonlinear activations can solve this problem.
13
+ # To approximate more complex functions, keep the number of inputs and outputs
14
+ # fixed (they depend on the problem) and increase the number and/or size of
15
+ # hidden neurons. For example: [2, 10, 7, 4, 1].
16
+ # NOTE: If your network grows above few thousands of weights, XNES may be too slow.
17
+ # Try using SNES for large shallow networks or BDNES for deep networks.
18
18
  NET = FFNN.new [2,2,1], act_fn: :logistic
19
19
  # Note: the process is exactly the same, from instantiation to training, for recurrent
20
20
  # networks using the class `WB::NeuralNetwork::Recursive`.
@@ -35,8 +35,8 @@ def fitness weights
35
35
  # - observation: correct value, our target
36
36
  pred_obs = XOR.map do |input, obs|
37
37
  # The network can have an arbitrary number of output neurons
38
- # Since here we have only one, we extract the value calling `#first`
39
- output = NET.activate(input).first
38
+ # Since here we have only one, we extract the value as the output
39
+ output = NET.activate(input)[0]
40
40
  # Here we interpret the output as classification
41
41
  pred = output > 0.5 ? 1 : 0
42
42
  # Finally accumulate prediction-observation pairs
@@ -50,9 +50,12 @@ end
50
50
  # Next comes initializing the black-box stochastic optimization algorithm
51
51
  # We are searching for the network's weights, this gives us the search space dimensionality
52
52
  # We'll use XNES as we are working with less than 100 dimensions (weights)
53
- nes = XNES.new NET.nweights, method(:fitness), :max, rseed: 15
54
- # Note: the random seed is fixed here to ensure the task is solved in one try in few iterations
55
- # In a real task, best using an over-large network, more iterations, and try several seeds
53
+ nes = XNES.new NET.nweights, method(:fitness), :max, rseed: 0
54
+ # Note BDNES requires `NET.nweights_per_layer` rather than `NET.nweights` in initialization:
55
+ # nes = WB::Optimizer::NaturalEvolutionStrategies::BDNES.new NET.nweights_per_layer,
56
+ # method(:fitness), :max, rseed: 10
57
+ # The random seed is fixed here to ensure a reproducible behavior
58
+ # In a real task, best using an oversized network, more iterations, and try several seeds
56
59
 
57
60
  # NOTE: In practical applications it is best to delegate parallelization to the fitness
58
61
  # function instead of computing the fitness of one individual at a time. This can be
@@ -60,16 +63,20 @@ nes = XNES.new NET.nweights, method(:fitness), :max, rseed: 15
60
63
  # setting the `parallel_fit` switch to `true`:
61
64
  # nes = XNES.new NET.nweights,
62
65
  # -> (genotypes) { Parallel.map genotypes, &method(:fitness) },
63
- # :max, rseed: 15, parallel_fit: true
66
+ # :max, rseed: 0, parallel_fit: true
64
67
 
65
68
 
66
- # Nothing left but to run the optimization algorithm, few epochs here will suffice
69
+ # Nothing left but to run the optimization algorithm
70
+ # Depending on the random seed (read: luck)few epochs here will suffice
67
71
  50.times { nes.train }
68
72
  # OK! now remember, `NET` currently holds the weights of the last evaluation
69
73
  # Let's fetch the best individual found so far
70
74
  best_fit, best_weights = nes.best
71
75
  # Let's run them again to check they work
72
- result = fitness best_weights # careful here if you defined a parallel `fitness`
73
- puts "The found network achieves a score of #{result} out of 4 in the XOR task"
74
- puts "Weights: #{best_weights}"
76
+ result = fitness best_weights
77
+ # Note if you defined a parallel fitness above you'll need instead
78
+ # result = fitness([best_weights])[0]
79
+ puts "The found network achieves a score of #{result} out of #{XOR.size} in the XOR task"
80
+ puts "Weights: #{best_weights.to_a}"
75
81
  puts "Done!"
82
+ # That's it! 18 lines and you got a working neuroevolution algorithm, congrats :)
@@ -35,7 +35,7 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
35
35
 
36
36
  # Box-Muller transform: generates standard (unit) normal distribution samples
37
37
  # @return [Float] a single sample from a standard normal distribution
38
- # @note Xumo::NArray implements this :) glad to have switched!
38
+ # @note Xumo::NArray implements this but no random seed selection yet
39
39
  def standard_normal_sample
40
40
  rho = Math.sqrt(-2.0 * Math.log(rng.rand))
41
41
  theta = 2 * Math::PI * rng.rand
@@ -80,7 +80,7 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
80
80
  # Samples a standard normal distribution to construct a NArray of
81
81
  # popsize multivariate samples of length ndims
82
82
  # @return [NArray] standard normal samples
83
- # @note Xumo::NArray implements this :) glad to have switched!
83
+ # @note Xumo::NArray implements this but no random seed selection yet
84
84
  def standard_normal_samples
85
85
  NArray.zeros([popsize, ndims]).tap do |ret|
86
86
  ret.each_with_index { |_,*i| ret[*i] = standard_normal_sample }
@@ -104,8 +104,9 @@ module MachineLearningWorkbench::Optimizer::NaturalEvolutionStrategies
104
104
  # matched with individuals sorted by INCREASING fitness. Then reverse order for minimization.
105
105
  # @return standard normal samples sorted by the respective individuals' fitnesses
106
106
  def sorted_inds
107
- # samples = standard_normal_samples # Xumo::NArray implements the Box-Muller :)
108
- samples = NArray.new([popsize, ndims]).rand_norm(0,1)
107
+ # Xumo::NArray implements the Box-Muller, but no random seed (yet)
108
+ samples = standard_normal_samples
109
+ # samples = NArray.new([popsize, ndims]).rand_norm(0,1)
109
110
  inds = move_inds(samples)
110
111
  fits = parallel_fit ? obj_fn.call(inds) : inds.map(&obj_fn)
111
112
  # Quick cure for NaN fitnesses
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.5.0
4
+ version: 0.5.1
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-04-05 00:00:00.000000000 Z
11
+ date: 2018-04-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler