ai4r 1.1 → 1.2

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.
Files changed (140) hide show
  1. data/README.rdoc +21 -20
  2. data/examples/decision_trees/id3_example.rb +3 -2
  3. data/examples/genetic_algorithm/genetic_algorithm_example.rb +6 -6
  4. data/examples/neural_network/backpropagation_example.rb +2 -2
  5. data/lib/ai4r/classifiers/classifier_helper.rb +54 -0
  6. data/lib/ai4r/classifiers/id3.rb +356 -0
  7. data/lib/ai4r/classifiers/one_r.rb +148 -0
  8. data/lib/ai4r/classifiers/prism.rb +231 -0
  9. data/lib/ai4r/classifiers/zero_r.rb +104 -0
  10. data/lib/ai4r/genetic_algorithm/genetic_algorithm.rb +272 -0
  11. data/lib/ai4r/neural_network/backpropagation.rb +271 -0
  12. data/site/build/tmp/locationmap.xml +14 -14
  13. data/site/build/tmp/output.xmap +23 -23
  14. data/site/build/tmp/pluginlist2fetchbuild.xml +144 -144
  15. data/site/build/tmp/plugins-1.xml +0 -11
  16. data/site/build/tmp/plugins-2.xml +54 -0
  17. data/site/build/tmp/projfilters.properties +41 -41
  18. data/site/build/webapp/WEB-INF/logs/core.log +681 -788
  19. data/site/build/webapp/WEB-INF/logs/error.log +281 -248
  20. data/site/build/webapp/WEB-INF/logs/sitemap.log +1015 -0
  21. data/site/src/documentation/content/xdocs/forum.html +9 -0
  22. data/site/src/documentation/content/xdocs/geneticAlgorithms.xml +82 -68
  23. data/site/src/documentation/content/xdocs/index.xml +47 -18
  24. data/site/src/documentation/content/xdocs/machineLearning.xml +10 -9
  25. data/site/src/documentation/content/xdocs/neuralNetworks.xml +60 -36
  26. data/site/src/documentation/content/xdocs/site.xml +8 -5
  27. data/site/src/documentation/content/xdocs/svn.xml +11 -1
  28. data/site/src/documentation/resources/images/Thumbs.db +0 -0
  29. data/site/src/documentation/resources/images/ai4r-logo.png +0 -0
  30. data/site/src/documentation/resources/images/genetic_algorithms_example.png +0 -0
  31. data/site/src/documentation/resources/images/jadeferret.png +0 -0
  32. data/site/src/documentation/resources/images/neural_network_example.png +0 -0
  33. data/site/src/documentation/resources/images/sub-dir/Thumbs.db +0 -0
  34. data/site/src/documentation/skinconf.xml +18 -18
  35. data/test/classifiers/id3_test.rb +206 -0
  36. data/test/classifiers/one_r_test.rb +62 -0
  37. data/test/classifiers/prism_test.rb +83 -0
  38. data/test/classifiers/zero_r_test.rb +48 -0
  39. data/test/genetic_algorithm/chromosome_test.rb +41 -38
  40. data/test/genetic_algorithm/genetic_algorithm_test.rb +64 -61
  41. data/test/neural_network/backpropagation_test.rb +20 -18
  42. metadata +109 -199
  43. data/lib/decision_tree/id3.rb +0 -354
  44. data/lib/genetic_algorithm/genetic_algorithm.rb +0 -268
  45. data/lib/neural_network/backpropagation.rb +0 -264
  46. data/site/build/site/en/broken-links.xml +0 -2
  47. data/site/build/site/en/downloads.html +0 -187
  48. data/site/build/site/en/downloads.pdf +0 -151
  49. data/site/build/site/en/geneticAlgorithms.html +0 -564
  50. data/site/build/site/en/geneticAlgorithms.pdf +0 -911
  51. data/site/build/site/en/images/ai4r-logo.png +0 -0
  52. data/site/build/site/en/images/built-with-forrest-button.png +0 -0
  53. data/site/build/site/en/images/c.png +0 -0
  54. data/site/build/site/en/images/c_wbn.png +0 -0
  55. data/site/build/site/en/images/c_wn.png +0 -0
  56. data/site/build/site/en/images/ero.gif +0 -0
  57. data/site/build/site/en/images/europe2.png +0 -0
  58. data/site/build/site/en/images/europe3.png +0 -0
  59. data/site/build/site/en/images/fitness.png +0 -0
  60. data/site/build/site/en/images/instruction_arrow.png +0 -0
  61. data/site/build/site/en/images/my_email.png +0 -0
  62. data/site/build/site/en/images/rubyforge.png +0 -0
  63. data/site/build/site/en/images/s.png +0 -0
  64. data/site/build/site/en/images/s_wbn.png +0 -0
  65. data/site/build/site/en/images/s_wn.png +0 -0
  66. data/site/build/site/en/images/sigmoid.png +0 -0
  67. data/site/build/site/en/images/t.png +0 -0
  68. data/site/build/site/en/images/t_wbn.png +0 -0
  69. data/site/build/site/en/images/t_wn.png +0 -0
  70. data/site/build/site/en/index.html +0 -258
  71. data/site/build/site/en/index.pdf +0 -306
  72. data/site/build/site/en/linkmap.html +0 -231
  73. data/site/build/site/en/linkmap.pdf +0 -94
  74. data/site/build/site/en/locationmap.xml +0 -72
  75. data/site/build/site/en/machineLearning.html +0 -325
  76. data/site/build/site/en/machineLearning.pdf +0 -337
  77. data/site/build/site/en/neuralNetworks.html +0 -446
  78. data/site/build/site/en/neuralNetworks.pdf +0 -604
  79. data/site/build/site/en/skin/CommonMessages_de.xml +0 -23
  80. data/site/build/site/en/skin/CommonMessages_en_US.xml +0 -23
  81. data/site/build/site/en/skin/CommonMessages_es.xml +0 -23
  82. data/site/build/site/en/skin/CommonMessages_fr.xml +0 -23
  83. data/site/build/site/en/skin/basic.css +0 -166
  84. data/site/build/site/en/skin/breadcrumbs-optimized.js +0 -90
  85. data/site/build/site/en/skin/breadcrumbs.js +0 -237
  86. data/site/build/site/en/skin/fontsize.js +0 -166
  87. data/site/build/site/en/skin/getBlank.js +0 -40
  88. data/site/build/site/en/skin/getMenu.js +0 -45
  89. data/site/build/site/en/skin/images/README.txt +0 -1
  90. data/site/build/site/en/skin/images/add.jpg +0 -0
  91. data/site/build/site/en/skin/images/built-with-forrest-button.png +0 -0
  92. data/site/build/site/en/skin/images/chapter.gif +0 -0
  93. data/site/build/site/en/skin/images/chapter_open.gif +0 -0
  94. data/site/build/site/en/skin/images/current.gif +0 -0
  95. data/site/build/site/en/skin/images/error.png +0 -0
  96. data/site/build/site/en/skin/images/external-link.gif +0 -0
  97. data/site/build/site/en/skin/images/fix.jpg +0 -0
  98. data/site/build/site/en/skin/images/forrest-credit-logo.png +0 -0
  99. data/site/build/site/en/skin/images/hack.jpg +0 -0
  100. data/site/build/site/en/skin/images/header_white_line.gif +0 -0
  101. data/site/build/site/en/skin/images/info.png +0 -0
  102. data/site/build/site/en/skin/images/instruction_arrow.png +0 -0
  103. data/site/build/site/en/skin/images/label.gif +0 -0
  104. data/site/build/site/en/skin/images/page.gif +0 -0
  105. data/site/build/site/en/skin/images/pdfdoc.gif +0 -0
  106. data/site/build/site/en/skin/images/poddoc.png +0 -0
  107. data/site/build/site/en/skin/images/printer.gif +0 -0
  108. data/site/build/site/en/skin/images/rc-b-l-15-1body-2menu-3menu.png +0 -0
  109. data/site/build/site/en/skin/images/rc-b-r-15-1body-2menu-3menu.png +0 -0
  110. data/site/build/site/en/skin/images/rc-b-r-5-1header-2tab-selected-3tab-selected.png +0 -0
  111. data/site/build/site/en/skin/images/rc-t-l-5-1header-2searchbox-3searchbox.png +0 -0
  112. data/site/build/site/en/skin/images/rc-t-l-5-1header-2tab-selected-3tab-selected.png +0 -0
  113. data/site/build/site/en/skin/images/rc-t-l-5-1header-2tab-unselected-3tab-unselected.png +0 -0
  114. data/site/build/site/en/skin/images/rc-t-r-15-1body-2menu-3menu.png +0 -0
  115. data/site/build/site/en/skin/images/rc-t-r-5-1header-2searchbox-3searchbox.png +0 -0
  116. data/site/build/site/en/skin/images/rc-t-r-5-1header-2tab-selected-3tab-selected.png +0 -0
  117. data/site/build/site/en/skin/images/rc-t-r-5-1header-2tab-unselected-3tab-unselected.png +0 -0
  118. data/site/build/site/en/skin/images/remove.jpg +0 -0
  119. data/site/build/site/en/skin/images/rss.png +0 -0
  120. data/site/build/site/en/skin/images/spacer.gif +0 -0
  121. data/site/build/site/en/skin/images/success.png +0 -0
  122. data/site/build/site/en/skin/images/txtdoc.png +0 -0
  123. data/site/build/site/en/skin/images/update.jpg +0 -0
  124. data/site/build/site/en/skin/images/valid-html401.png +0 -0
  125. data/site/build/site/en/skin/images/vcss.png +0 -0
  126. data/site/build/site/en/skin/images/warning.png +0 -0
  127. data/site/build/site/en/skin/images/xmldoc.gif +0 -0
  128. data/site/build/site/en/skin/menu.js +0 -48
  129. data/site/build/site/en/skin/note.txt +0 -50
  130. data/site/build/site/en/skin/print.css +0 -54
  131. data/site/build/site/en/skin/profile.css +0 -163
  132. data/site/build/site/en/skin/prototype.js +0 -1257
  133. data/site/build/site/en/skin/screen.css +0 -587
  134. data/site/build/site/en/svn.html +0 -223
  135. data/site/build/site/en/svn.pdf +0 -239
  136. data/site/build/site/en/wholesite.pdf +0 -1686
  137. data/site/build/tmp/brokenlinks.xml +0 -2
  138. data/site/build/tmp/cocoon-work/cache-dir/cocoon-ehcache-1.data +0 -0
  139. data/site/build/tmp/cocoon-work/cache-dir/cocoon-ehcache-1.index +0 -0
  140. data/test/decision_tree/id3_test.rb +0 -209
@@ -0,0 +1,272 @@
1
+ #
2
+ # The GeneticAlgorithm module implements the GeneticSearch and Chromosome
3
+ # classes. The GeneticSearch is a generic class, and can be used to solved
4
+ # any kind of problems. The GeneticSearch class performs a stochastic search
5
+ # of the solution of a given problem.
6
+ #
7
+ # The Chromosome is "problem specific". Ai4r built-in Chromosomeclass was
8
+ # designed to model the Travelling salesman problem. If you want to solve other
9
+ # type of problem, you will have to modify the Chromosome class, by overwriting
10
+ # its fitness, reproduce, and mutate functions, to model you specific problem.
11
+ #
12
+ # Author:: Sergio Fierens
13
+ # License:: MPL 1.1
14
+ # Project:: ai4r
15
+ # Url:: http://ai4r.rubyforge.org/
16
+ #
17
+ # You can redistribute it and/or modify it under the terms of
18
+ # the Mozilla Public License version 1.1 as published by the
19
+ # Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
20
+
21
+ module Ai4r
22
+
23
+ module GeneticAlgorithm
24
+
25
+ # This class is used to automatically:
26
+ #
27
+ # 1. Choose initial population
28
+ # 2. Evaluate the fitness of each individual in the population
29
+ # 3. Repeat
30
+ # 1. Select best-ranking individuals to reproduce
31
+ # 2. Breed new generation through crossover and mutation (genetic operations) and give birth to offspring
32
+ # 3. Evaluate the individual fitnesses of the offspring
33
+ # 4. Replace worst ranked part of population with offspring
34
+ # 4. Until termination
35
+ #
36
+ # If you want to customize the algorithm, you must modify any of the following classes:
37
+ # - Chromosome
38
+ # - Population
39
+ class GeneticSearch
40
+
41
+ attr_accessor :population
42
+
43
+
44
+ def initialize(initial_population_size, generations)
45
+ @population_size = initial_population_size
46
+ @max_generation = generations
47
+ @generation = 0
48
+ end
49
+
50
+ # 1. Choose initial population
51
+ # 2. Evaluate the fitness of each individual in the population
52
+ # 3. Repeat
53
+ # 1. Select best-ranking individuals to reproduce
54
+ # 2. Breed new generation through crossover and mutation (genetic operations) and give birth to offspring
55
+ # 3. Evaluate the individual fitnesses of the offspring
56
+ # 4. Replace worst ranked part of population with offspring
57
+ # 4. Until termination
58
+ # 5. Return the best chromosome
59
+ def run
60
+ generate_initial_population #Generate initial population
61
+ @max_generation.times do
62
+ selected_to_breed = selection #Evaluates current population
63
+ offsprings = reproduction selected_to_breed #Generate the population for this new generation
64
+ replace_worst_ranked offsprings
65
+ end
66
+ return best_chromosome
67
+ end
68
+
69
+
70
+ def generate_initial_population
71
+ @population = []
72
+ @population_size.times do
73
+ population << Chromosome.seed
74
+ end
75
+ end
76
+
77
+ # Select best-ranking individuals to reproduce
78
+ #
79
+ # Selection is the stage of a genetic algorithm in which individual
80
+ # genomes are chosen from a population for later breeding.
81
+ # There are several generic selection algorithms, such as
82
+ # tournament selection and roulette wheel selection. We implemented the
83
+ # latest.
84
+ #
85
+ # Steps:
86
+ #
87
+ # 1. The fitness function is evaluated for each individual, providing fitness values
88
+ # 2. The population is sorted by descending fitness values.
89
+ # 3. The fitness values ar then normalized. (Highest fitness gets 1, lowest fitness gets 0). The normalized value is stored in the "normalized_fitness" attribute of the chromosomes.
90
+ # 4. A random number R is chosen. R is between 0 and the accumulated normalized value (all the normalized fitness values added togheter).
91
+ # 5. The selected individual is the first one whose accumulated normalized value (its is normalized value plus the normalized values of the chromosomes prior it) greater than R.
92
+ # 6. We repeat steps 4 and 5, 2/3 times the population size.
93
+ def selection
94
+ @population.sort! { |a, b| b.fitness <=> a.fitness}
95
+ best_fitness = @population[0].fitness
96
+ worst_fitness = @population.last.fitness
97
+ acum_fitness = 0
98
+ if best_fitness-worst_fitness > 0
99
+ @population.each do |chromosome|
100
+ chromosome.normalized_fitness = (chromosome.fitness - worst_fitness)/(best_fitness-worst_fitness)
101
+ acum_fitness += chromosome.normalized_fitness
102
+ end
103
+ else
104
+ @population.each { |chromosome| chromosome.normalized_fitness = 1}
105
+ end
106
+ selected_to_breed = []
107
+ ((2*@population_size)/3).times do
108
+ selected_to_breed << select_random_individual(acum_fitness)
109
+ end
110
+ selected_to_breed
111
+ end
112
+
113
+ # We combine each pair of selected chromosome using the method
114
+ # Chromosome.reproduce
115
+ #
116
+ # The reproduction will also call the Chromosome.mutate method with
117
+ # each member of the population. You should implement Chromosome.mutate
118
+ # to only change (mutate) randomly. E.g. You could effectivly change the
119
+ # chromosome only if
120
+ # rand < ((1 - chromosome.normalized_fitness) * 0.4)
121
+ def reproduction(selected_to_breed)
122
+ offsprings = []
123
+ 0.upto(selected_to_breed.length/2-1) do |i|
124
+ offsprings << Chromosome.reproduce(selected_to_breed[2*i], selected_to_breed[2*i+1])
125
+ end
126
+ @population.each do |individual|
127
+ Chromosome.mutate(individual)
128
+ end
129
+ return offsprings
130
+ end
131
+
132
+ # Replace worst ranked part of population with offspring
133
+ def replace_worst_ranked(offsprings)
134
+ size = offsprings.length
135
+ @population = @population [0..((-1*size)-1)] + offsprings
136
+ end
137
+
138
+ # Select the best chromosome in the population
139
+ def best_chromosome
140
+ the_best = @population[0]
141
+ @population.each do |chromosome|
142
+ the_best = chromosome if chromosome.fitness > the_best.fitness
143
+ end
144
+ return the_best
145
+ end
146
+
147
+ private
148
+ def select_random_individual(acum_fitness)
149
+ select_random_target = acum_fitness * rand
150
+ local_acum = 0
151
+ @population.each do |chromosome|
152
+ local_acum += chromosome.normalized_fitness
153
+ return chromosome if local_acum >= select_random_target
154
+ end
155
+ end
156
+
157
+ end
158
+
159
+ # A Chromosome is a representation of an individual solutions for a specific
160
+ # problem. You will have to redifine you Chromosome representation for each
161
+ # particular problem, along with its fitness, mutate, reproduce, and seed
162
+ # functions.
163
+ class Chromosome
164
+
165
+ attr_accessor :data
166
+ attr_accessor :normalized_fitness
167
+
168
+ def initialize(data)
169
+ @data = data
170
+ end
171
+
172
+ # The fitness function quantifies the optimality of a solution
173
+ # (that is, a chromosome) in a genetic algorithm so that that particular
174
+ # chromosome may be ranked against all the other chromosomes.
175
+ #
176
+ # Optimal chromosomes, or at least chromosomes which are more optimal,
177
+ # are allowed to breed and mix their datasets by any of several techniques,
178
+ # producing a new generation that will (hopefully) be even better.
179
+ def fitness
180
+ return @fitness if @fitness
181
+ last_token = @data[0]
182
+ cost = 0
183
+ @data[1..-1].each do |token|
184
+ cost += @@costs[last_token][token]
185
+ last_token = token
186
+ end
187
+ @fitness = -1 * cost
188
+ return @fitness
189
+ end
190
+
191
+ # mutation is a function used to maintain genetic diversity from one
192
+ # generation of a population of chromosomes to the next. It is analogous
193
+ # to biological mutation.
194
+ #
195
+ # The purpose of mutation in GAs is to allow the
196
+ # algorithm to avoid local minima by preventing the population of
197
+ # chromosomes from becoming too similar to each other, thus slowing or even
198
+ # stopping evolution.
199
+ #
200
+ # Calling the mutate function will "probably" slightly change a chromosome
201
+ # randomly.
202
+ #
203
+ # This implementation of "mutation" will (probably) reverse the
204
+ # order of 2 consecutive randome nodes
205
+ # (e.g. from [ 0, 1, 2, 4] to [0, 2, 1, 4]) if:
206
+ # ((1 - chromosome.normalized_fitness) * 0.4)
207
+ def self.mutate(chromosome)
208
+ if chromosome.normalized_fitness && rand < ((1 - chromosome.normalized_fitness) * 0.3)
209
+ data = chromosome.data
210
+ index = rand(data.length-1)
211
+ data[index], data[index+1] = data[index+1], data[index]
212
+ chromosome.data = data
213
+ @fitness = nil
214
+ end
215
+ end
216
+
217
+ # Reproduction is used to vary the programming of a chromosome or
218
+ # chromosomes from one generation to the next. There are several ways to
219
+ # combine two chromosomes: One-point crossover, Two-point crossover,
220
+ # "Cut and splice", edge recombination, and more.
221
+ #
222
+ # The method is usually dependant of the problem domain.
223
+ # In this case, we have implemented edge recombination, wich is the
224
+ # most used reproduction algorithm for the Travelling salesman problem.
225
+ def self.reproduce(a, b)
226
+ data_size = @@costs[0].length
227
+ available = []
228
+ 0.upto(data_size-1) { |n| available << n }
229
+ token = a.data[0]
230
+ spawn = [token]
231
+ available.delete(token)
232
+ while available.length > 0 do
233
+ #Select next
234
+ if token != b.data.last && available.include?(b.data[b.data.index(token)+1])
235
+ next_token = b.data[b.data.index(token)+1]
236
+ elsif token != a.data.last && available.include?(a.data[a.data.index(token)+1])
237
+ next_token = a.data[a.data.index(token)+1]
238
+ else
239
+ next_token = available[rand(available.length)]
240
+ end
241
+ #Add to spawn
242
+ token = next_token
243
+ available.delete(token)
244
+ spawn << next_token
245
+ a, b = b, a if rand < 0.4
246
+ end
247
+ return Chromosome.new(spawn)
248
+ end
249
+
250
+ # Initializes an individual solution (chromosome) for the initial
251
+ # population. Usually the chromosome is generated randomly, but you can
252
+ # use some problem domain knowledge, to generate better initial solutions.
253
+ def self.seed
254
+ data_size = @@costs[0].length
255
+ available = []
256
+ 0.upto(data_size-1) { |n| available << n }
257
+ seed = []
258
+ while available.length > 0 do
259
+ index = rand(available.length)
260
+ seed << available.delete_at(index)
261
+ end
262
+ return Chromosome.new(seed)
263
+ end
264
+
265
+ def self.set_cost_matrix(costs)
266
+ @@costs = costs
267
+ end
268
+ end
269
+
270
+ end
271
+
272
+ end
@@ -0,0 +1,271 @@
1
+ # The utility of artificial neural network
2
+ # models lies in the fact that they can be used
3
+ # to infer a function from observations.
4
+ # This is particularly useful in applications
5
+ # where the complexity of the data or task makes the
6
+ # design of such a function by hand impractical.
7
+ # Neural Networks are being used in many businesses and applications. Their
8
+ # ability to learn by example makes them attractive in environments where
9
+ # the business rules are either not well defined or are hard to enumerate and
10
+ # define. Many people believe that Neural Networks can only solve toy problems.
11
+ # Give them a try, and let you decide if they are good enough to solve your
12
+ # needs.
13
+ #
14
+ # In this module you will find an implementation of neural networks
15
+ # using the Backpropagation is a supervised learning technique (described
16
+ # by Paul Werbos in 1974, and further developed by David E.
17
+ # Rumelhart, Geoffrey E. Hinton and Ronald J. Williams in 1986)
18
+ #
19
+ # More about neural networks and backpropagation:
20
+ #
21
+ # * http://en.wikipedia.org/wiki/Backpropagation
22
+ # * http://en.wikipedia.org/wiki/Neural_networks
23
+ #
24
+ # Author:: Sergio Fierens
25
+ # License:: MPL 1.1
26
+ # Project:: ai4r
27
+ # Url:: http://ai4r.rubyforge.org/
28
+ #
29
+ # Specials thanks to John Miller, for several bugs fixes and comments in the
30
+ # Backpropagation implementation
31
+ #
32
+ # You can redistribute it and/or modify it under the terms of
33
+ # the Mozilla Public License version 1.1 as published by the
34
+ # Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
35
+ #
36
+
37
+ module Ai4r
38
+
39
+ module NeuralNetwork
40
+
41
+ # = Introduction
42
+ #
43
+ # This is an implementation of neural networks
44
+ # using the Backpropagation is a supervised learning technique (described
45
+ # by Paul Werbos in 1974, and further developed by David E.
46
+ # Rumelhart, Geoffrey E. Hinton and Ronald J. Williams in 1986)
47
+ #
48
+ # = How to use it
49
+ #
50
+ # # Create the network
51
+ # net = Backpropagation.new([4, 3, 2]) # 4 inputs
52
+ # # 1 hidden layer with 3 neurons,
53
+ # # 2 outputs
54
+ # # Train the network
55
+ # 1..upto(100) do |i|
56
+ # net.train(example[i], result[i])
57
+ # end
58
+ #
59
+ # # Use it: Evaluate data with the trained network
60
+ # net.eval([12, 48, 12, 25]) # => [0.86, 0.01]
61
+ #
62
+ class Backpropagation
63
+
64
+ DEFAULT_BETA = 0.5
65
+ DEFAULT_LAMBDA = 0.25
66
+ DEFAULT_THRESHOLD = 0.66
67
+
68
+ # Creates a new network specifying the its architecture.
69
+ # E.g.
70
+ #
71
+ # net = Backpropagation.new([4, 3, 2]) # 4 inputs
72
+ # # 1 hidden layer with 3 neurons,
73
+ # # 2 outputs
74
+ # net = Backpropagation.new([2, 3, 3, 4]) # 2 inputs
75
+ # # 2 hidden layer with 3 neurons each,
76
+ # # 4 outputs
77
+ # net = Backpropagation.new([2, 1]) # 2 inputs
78
+ # # No hidden layer
79
+ # # 1 output
80
+ #
81
+ # Optionally you can customize certain parameters:
82
+ #
83
+ # threshold = A real number which we will call Threshold.
84
+ # Experiments have shown that best values for q are between 0.25 and 1.
85
+ #
86
+ # lambda = The Learning Rate: a real number, usually between 0.05 and 0.25.
87
+ #
88
+ # momentum = A momentum will avoid oscillations during learning, converging
89
+ # to a solution in less iterations.
90
+ def initialize(layer_sizes, threshold=DEFAULT_THRESHOLD, lambda=DEFAULT_LAMBDA, momentum=DEFAULT_BETA)
91
+ @neurons = []
92
+ layer_sizes.reverse.each do |layer_size|
93
+ layer = []
94
+ layer_size.times { layer << Neuron.new(@neurons.last, threshold, lambda, momentum) }
95
+ @neurons << layer
96
+ end
97
+ @neurons.reverse!
98
+ end
99
+
100
+ # Evaluates the input.
101
+ # E.g.
102
+ # net = Backpropagation.new([4, 3, 2])
103
+ # net.eval([25, 32.3, 12.8, 1.5])
104
+ # # => [0.83, 0.03]
105
+ def eval(input)
106
+ #check input size
107
+ if(input.length != @neurons.first.length)
108
+ raise "Wrong input dimension. Expected: #{@neurons.first.length}, received: #{input.length}"
109
+ end
110
+ #Present input
111
+ input.each_index do |input_index|
112
+ @neurons.first[input_index].propagate(input[input_index])
113
+ end
114
+ #Propagate
115
+ @neurons[1..-1].each do |layer|
116
+ layer.each {|neuron| neuron.propagate}
117
+ end
118
+ output = []
119
+ @neurons.last.each { |neuron| output << neuron.state }
120
+ return output
121
+ end
122
+
123
+ # This method trains the network using the backpropagation algorithm.
124
+ #
125
+ # input: Networks input
126
+ #
127
+ # output: Expected output for the given input.
128
+ #
129
+ # This method returns the network error (not an absolut amount,
130
+ # the difference between real output and the expected output)
131
+ def train(input, output)
132
+ #check output size
133
+ if(output.length != @neurons.last.length)
134
+ raise "Wrong output dimension. Expected: #{@neurons.last.length}, received: #{output.length}"
135
+ end
136
+ #Eval input
137
+ eval(input)
138
+ #Set expected output
139
+ output.each_index do |output_index|
140
+ @neurons.last[output_index].expected_output = output[output_index]
141
+ end
142
+ #Calculate error
143
+ @neurons.reverse.each do |layer|
144
+ layer.each {|neuron| neuron.calc_error}
145
+ end
146
+ #Change weight
147
+ @neurons.each do |layer|
148
+ layer.each {|neuron| neuron.change_weights }
149
+ end
150
+ #return net error
151
+ return @neurons.last.collect { |x| x.calc_error }
152
+ end
153
+
154
+ private
155
+ def print_weight
156
+ @neurons.each_index do |layer_index|
157
+ @neurons[layer_index].each_index do |neuron_index|
158
+ puts "L #{layer_index} N #{neuron_index} W #{@neurons[layer_index][neuron_index].w.inspect}"
159
+ end
160
+ end
161
+ end
162
+
163
+ end
164
+
165
+
166
+ class Neuron
167
+
168
+ attr_accessor :state
169
+ attr_accessor :error
170
+ attr_accessor :expected_output
171
+ attr_accessor :w
172
+ attr_accessor :x
173
+
174
+ def initialize(childs, threshold, lambda, momentum)
175
+ #instance state
176
+ @w = nil
177
+ @childs = childs
178
+ @error = nil
179
+ @state = 0
180
+ @pushed = 0
181
+ @last_delta = 0
182
+ @x = 0
183
+ #Parameters
184
+ @lambda = lambda
185
+ @momentum = momentum
186
+ @threshold = threshold
187
+ #init w
188
+ if(childs)
189
+ @w = []
190
+ childs.each { @w << init_weight }
191
+ end
192
+ end
193
+
194
+ def push(x)
195
+ @pushed += x
196
+ end
197
+
198
+ def propagate(input = nil)
199
+ if(input)
200
+ input = input.to_f
201
+ @x = input
202
+ @state = input
203
+ @childs.each_index do |child_index|
204
+ @childs[child_index].push(input * @w[child_index])
205
+ end
206
+ else
207
+ @x = @pushed + @threshold
208
+ @pushed = 0
209
+ @state = Neuron.f(@x)
210
+ if @childs
211
+ @childs.each_index do |child_index|
212
+ @childs[child_index].push(@state * @w[child_index])
213
+ end
214
+ end
215
+ end
216
+ end
217
+
218
+ def calc_error
219
+ if(!@childs && @expected_output)
220
+ @error = (@expected_output - @state)
221
+ elsif(@childs)
222
+ @error = 0
223
+ @childs.each_index do |child_index|
224
+ @error += (@childs[child_index].error * @w[child_index])
225
+ end
226
+ end
227
+ end
228
+
229
+ def change_weights
230
+ return if !@childs
231
+ @childs.each_index do |child_index |
232
+ delta = @lambda * @childs[child_index].error * (@state) * Neuron.f_prime(@childs[child_index].x)
233
+ @w[child_index] += (delta + @momentum * @last_delta)
234
+ @last_delta = delta
235
+ end
236
+ end
237
+
238
+ # Propagation function.
239
+ # By default:
240
+ # f(x) = 1/(1 + e^(-x))
241
+ # You can override it with any derivable function.
242
+ # A usually usefull one is:
243
+ # f(x) = x.
244
+ # If you override this function, you will have to override
245
+ # f_prime too.
246
+ def self.f(x)
247
+ return 1/(1+Math.exp(-1*(x)))
248
+ end
249
+
250
+ # Derived function of the propagation function (self.f)
251
+ # By default:
252
+ # f_prime(x) = f(x)(1- f(x))
253
+ # If you override f(x) with:
254
+ # f(x) = x.
255
+ # Then you must override f_prime as:
256
+ # f_prime(x) = 1
257
+ def self.f_prime(x)
258
+ val = f(x)
259
+ return val*(1-val)
260
+ end
261
+
262
+ private
263
+ def init_weight
264
+ rand/4
265
+ end
266
+
267
+ end
268
+
269
+ end
270
+
271
+ end