ai4r 1.12 → 2.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 +7 -0
- data/README.md +174 -0
- data/examples/classifiers/hyperpipes_data.csv +14 -0
- data/examples/classifiers/hyperpipes_example.rb +22 -0
- data/examples/classifiers/ib1_example.rb +12 -0
- data/examples/classifiers/id3_example.rb +15 -10
- data/examples/classifiers/id3_graphviz_example.rb +17 -0
- data/examples/classifiers/logistic_regression_example.rb +11 -0
- data/examples/classifiers/naive_bayes_attributes_example.rb +13 -0
- data/examples/classifiers/naive_bayes_example.rb +12 -13
- data/examples/classifiers/one_r_example.rb +27 -0
- data/examples/classifiers/parameter_tutorial.rb +29 -0
- data/examples/classifiers/prism_nominal_example.rb +15 -0
- data/examples/classifiers/prism_numeric_example.rb +21 -0
- data/examples/classifiers/simple_linear_regression_example.csv +159 -0
- data/examples/classifiers/simple_linear_regression_example.rb +18 -0
- data/examples/classifiers/zero_and_one_r_example.rb +34 -0
- data/examples/classifiers/zero_one_r_data.csv +8 -0
- data/examples/clusterers/clusterer_example.rb +62 -0
- data/examples/clusterers/dbscan_example.rb +17 -0
- data/examples/clusterers/dendrogram_example.rb +17 -0
- data/examples/clusterers/hierarchical_dendrogram_example.rb +20 -0
- data/examples/clusterers/kmeans_custom_example.rb +26 -0
- data/examples/genetic_algorithm/bitstring_example.rb +41 -0
- data/examples/genetic_algorithm/genetic_algorithm_example.rb +26 -18
- data/examples/genetic_algorithm/kmeans_seed_tuning.rb +45 -0
- data/examples/neural_network/backpropagation_example.rb +49 -48
- data/examples/neural_network/hopfield_example.rb +45 -0
- data/examples/neural_network/patterns_with_base_noise.rb +39 -39
- data/examples/neural_network/patterns_with_noise.rb +41 -39
- data/examples/neural_network/train_epochs_callback.rb +25 -0
- data/examples/neural_network/training_patterns.rb +39 -39
- data/examples/neural_network/transformer_text_classification.rb +78 -0
- data/examples/neural_network/xor_example.rb +23 -22
- data/examples/reinforcement/q_learning_example.rb +10 -0
- data/examples/som/som_data.rb +155 -152
- data/examples/som/som_multi_node_example.rb +12 -13
- data/examples/som/som_single_example.rb +12 -15
- data/examples/transformer/decode_classifier_example.rb +68 -0
- data/examples/transformer/deterministic_example.rb +10 -0
- data/examples/transformer/seq2seq_example.rb +16 -0
- data/lib/ai4r/classifiers/classifier.rb +24 -16
- data/lib/ai4r/classifiers/gradient_boosting.rb +64 -0
- data/lib/ai4r/classifiers/hyperpipes.rb +119 -43
- data/lib/ai4r/classifiers/ib1.rb +122 -32
- data/lib/ai4r/classifiers/id3.rb +527 -144
- data/lib/ai4r/classifiers/logistic_regression.rb +96 -0
- data/lib/ai4r/classifiers/multilayer_perceptron.rb +75 -59
- data/lib/ai4r/classifiers/naive_bayes.rb +112 -48
- data/lib/ai4r/classifiers/one_r.rb +112 -44
- data/lib/ai4r/classifiers/prism.rb +167 -76
- data/lib/ai4r/classifiers/random_forest.rb +72 -0
- data/lib/ai4r/classifiers/simple_linear_regression.rb +143 -0
- data/lib/ai4r/classifiers/support_vector_machine.rb +91 -0
- data/lib/ai4r/classifiers/votes.rb +57 -0
- data/lib/ai4r/classifiers/zero_r.rb +71 -30
- data/lib/ai4r/clusterers/average_linkage.rb +46 -27
- data/lib/ai4r/clusterers/bisecting_k_means.rb +50 -44
- data/lib/ai4r/clusterers/centroid_linkage.rb +52 -36
- data/lib/ai4r/clusterers/cluster_tree.rb +50 -0
- data/lib/ai4r/clusterers/clusterer.rb +28 -24
- data/lib/ai4r/clusterers/complete_linkage.rb +42 -31
- data/lib/ai4r/clusterers/dbscan.rb +134 -0
- data/lib/ai4r/clusterers/diana.rb +75 -49
- data/lib/ai4r/clusterers/k_means.rb +309 -72
- data/lib/ai4r/clusterers/median_linkage.rb +49 -33
- data/lib/ai4r/clusterers/single_linkage.rb +196 -88
- data/lib/ai4r/clusterers/ward_linkage.rb +51 -35
- data/lib/ai4r/clusterers/ward_linkage_hierarchical.rb +63 -0
- data/lib/ai4r/clusterers/weighted_average_linkage.rb +48 -32
- data/lib/ai4r/data/data_set.rb +229 -100
- data/lib/ai4r/data/parameterizable.rb +31 -25
- data/lib/ai4r/data/proximity.rb +72 -50
- data/lib/ai4r/data/statistics.rb +46 -35
- data/lib/ai4r/experiment/classifier_evaluator.rb +84 -32
- data/lib/ai4r/experiment/split.rb +39 -0
- data/lib/ai4r/genetic_algorithm/chromosome_base.rb +43 -0
- data/lib/ai4r/genetic_algorithm/genetic_algorithm.rb +92 -170
- data/lib/ai4r/genetic_algorithm/tsp_chromosome.rb +83 -0
- data/lib/ai4r/hmm/hidden_markov_model.rb +134 -0
- data/lib/ai4r/neural_network/activation_functions.rb +37 -0
- data/lib/ai4r/neural_network/backpropagation.rb +419 -143
- data/lib/ai4r/neural_network/hopfield.rb +175 -58
- data/lib/ai4r/neural_network/transformer.rb +194 -0
- data/lib/ai4r/neural_network/weight_initializations.rb +40 -0
- data/lib/ai4r/reinforcement/policy_iteration.rb +66 -0
- data/lib/ai4r/reinforcement/q_learning.rb +51 -0
- data/lib/ai4r/search/a_star.rb +76 -0
- data/lib/ai4r/search/bfs.rb +50 -0
- data/lib/ai4r/search/dfs.rb +50 -0
- data/lib/ai4r/search/mcts.rb +118 -0
- data/lib/ai4r/search.rb +12 -0
- data/lib/ai4r/som/distance_metrics.rb +29 -0
- data/lib/ai4r/som/layer.rb +28 -17
- data/lib/ai4r/som/node.rb +61 -32
- data/lib/ai4r/som/som.rb +158 -41
- data/lib/ai4r/som/two_phase_layer.rb +21 -25
- data/lib/ai4r/version.rb +3 -0
- data/lib/ai4r.rb +58 -27
- metadata +117 -106
- data/README.rdoc +0 -44
- data/test/classifiers/hyperpipes_test.rb +0 -84
- data/test/classifiers/ib1_test.rb +0 -78
- data/test/classifiers/id3_test.rb +0 -208
- data/test/classifiers/multilayer_perceptron_test.rb +0 -79
- data/test/classifiers/naive_bayes_test.rb +0 -43
- data/test/classifiers/one_r_test.rb +0 -62
- data/test/classifiers/prism_test.rb +0 -85
- data/test/classifiers/zero_r_test.rb +0 -50
- data/test/clusterers/average_linkage_test.rb +0 -51
- data/test/clusterers/bisecting_k_means_test.rb +0 -66
- data/test/clusterers/centroid_linkage_test.rb +0 -53
- data/test/clusterers/complete_linkage_test.rb +0 -57
- data/test/clusterers/diana_test.rb +0 -69
- data/test/clusterers/k_means_test.rb +0 -100
- data/test/clusterers/median_linkage_test.rb +0 -53
- data/test/clusterers/single_linkage_test.rb +0 -122
- data/test/clusterers/ward_linkage_test.rb +0 -53
- data/test/clusterers/weighted_average_linkage_test.rb +0 -53
- data/test/data/data_set_test.rb +0 -96
- data/test/data/proximity_test.rb +0 -81
- data/test/data/statistics_test.rb +0 -65
- data/test/experiment/classifier_evaluator_test.rb +0 -76
- data/test/genetic_algorithm/chromosome_test.rb +0 -57
- data/test/genetic_algorithm/genetic_algorithm_test.rb +0 -81
- data/test/neural_network/backpropagation_test.rb +0 -82
- data/test/neural_network/hopfield_test.rb +0 -72
- data/test/som/som_test.rb +0 -97
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../lib/ai4r/classifiers/zero_r'
|
4
|
+
require_relative '../../lib/ai4r/classifiers/one_r'
|
5
|
+
require_relative '../../lib/ai4r/data/data_set'
|
6
|
+
|
7
|
+
# Load tutorial data
|
8
|
+
data_file = "#{File.dirname(__FILE__)}/zero_one_r_data.csv"
|
9
|
+
data = Ai4r::Data::DataSet.new.load_csv_with_labels data_file
|
10
|
+
|
11
|
+
puts "Data labels: #{data.data_labels.inspect}"
|
12
|
+
puts
|
13
|
+
|
14
|
+
# Build a default ZeroR classifier
|
15
|
+
zero_default = Ai4r::Classifiers::ZeroR.new.build(data)
|
16
|
+
puts "ZeroR default prediction: #{zero_default.eval(data.data_items.first)}"
|
17
|
+
puts "Generated rule: #{zero_default.get_rules}"
|
18
|
+
|
19
|
+
# Build ZeroR with custom tie strategy
|
20
|
+
zero_random = Ai4r::Classifiers::ZeroR.new.set_parameters(tie_break: :random).build(data)
|
21
|
+
puts "ZeroR random tie strategy prediction: #{zero_random.eval(data.data_items.first)}"
|
22
|
+
|
23
|
+
puts
|
24
|
+
|
25
|
+
# Build a default OneR classifier
|
26
|
+
one_default = Ai4r::Classifiers::OneR.new.build(data)
|
27
|
+
puts "OneR chose attribute index #{one_default.rule[:attr_index]}"
|
28
|
+
puts "OneR rules:\n#{one_default.get_rules}"
|
29
|
+
|
30
|
+
# Build OneR selecting the first attribute and using :last tie break
|
31
|
+
one_custom = Ai4r::Classifiers::OneR.new.set_parameters(selected_attribute: 0,
|
32
|
+
tie_break: :last).build(data)
|
33
|
+
puts "OneR forced attribute: #{one_custom.rule[:attr_index]}"
|
34
|
+
puts "Custom rules:\n#{one_custom.get_rules}"
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Author:: Sergio Fierens (implementation)
|
4
|
+
# License:: MPL 1.1
|
5
|
+
# Project:: ai4r
|
6
|
+
# Url:: http://www.ai4r.org/
|
7
|
+
#
|
8
|
+
# You can redistribute it and/or modify it under the terms of
|
9
|
+
# the Mozilla Public License version 1.1 as published by the
|
10
|
+
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
11
|
+
|
12
|
+
# In this example we group results from a post-training survey into 4 groups.
|
13
|
+
# The Diana algorithm is used, but you can try other algorithms by changing
|
14
|
+
# the word "Diana" by "KMeans", "AverageLinkage", or any other cluster implementation.
|
15
|
+
# The cluster API is the same, so you can play around and observe different results.
|
16
|
+
|
17
|
+
require 'ai4r'
|
18
|
+
|
19
|
+
# 5 Questions on a post training survey
|
20
|
+
questions = ['The material covered was appropriate for someone with my level of ' \
|
21
|
+
'knowledge of the subject.',
|
22
|
+
'The material was presented in a clear and logical fashion',
|
23
|
+
'There was sufficient time in the session to cover the material that was presented',
|
24
|
+
'The instructor was respectful of students',
|
25
|
+
'The instructor provided good examples']
|
26
|
+
|
27
|
+
# Answers to each question go from 1 (bad) to 5 (excellent)
|
28
|
+
# The answers array has an element per survey complemented.
|
29
|
+
# Each survey completed is in turn an array with the answer of each question.
|
30
|
+
answers = [[1, 2, 3, 2, 2], # Answers of person 1
|
31
|
+
[5, 5, 3, 2, 2], # Answers of person 2
|
32
|
+
[1, 2, 3, 2, 2], # Answers of person 3
|
33
|
+
[1, 2, 2, 2, 2], # ...
|
34
|
+
[1, 2, 5, 5, 2],
|
35
|
+
[3, 3, 3, 3, 3],
|
36
|
+
[1, 2, 3, 2, 2],
|
37
|
+
[3, 2, 3, 5, 5],
|
38
|
+
[3, 3, 3, 5, 2],
|
39
|
+
[4, 4, 3, 1, 1],
|
40
|
+
[5, 5, 5, 5, 5],
|
41
|
+
[4, 2, 4, 2, 1],
|
42
|
+
[4, 4, 5, 5, 5],
|
43
|
+
[4, 4, 3, 2, 2],
|
44
|
+
[2, 2, 3, 2, 3],
|
45
|
+
[3, 3, 3, 1, 1]] # Answers of person 16
|
46
|
+
|
47
|
+
data_set = Ai4r::Data::DataSet.new(data_items: answers, data_labels: questions)
|
48
|
+
|
49
|
+
# Let's group answers in 4 groups
|
50
|
+
clusterer = Ai4r::Clusterers::Diana.new.build(data_set, 4)
|
51
|
+
|
52
|
+
clusterer.clusters.each_with_index do |cluster, index|
|
53
|
+
puts "Group #{index + 1}"
|
54
|
+
p cluster.data_items
|
55
|
+
end
|
56
|
+
|
57
|
+
# Check if this algorithm supports evaluating new data items
|
58
|
+
if clusterer.supports_eval?
|
59
|
+
puts "First survey belongs to group #{clusterer.eval(answers.first)}"
|
60
|
+
else
|
61
|
+
puts 'This algorithm does not support eval on unseen data.'
|
62
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Example showcasing DBSCAN clustering with custom parameters.
|
4
|
+
require 'ai4r'
|
5
|
+
|
6
|
+
points = [
|
7
|
+
[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3],
|
8
|
+
[8, 8], [8, 9], [8, 10], [9, 8], [9, 9], [9, 10],
|
9
|
+
[5, 5], [1, 9], [10, 0]
|
10
|
+
]
|
11
|
+
set = Ai4r::Data::DataSet.new(data_items: points)
|
12
|
+
|
13
|
+
clusterer = Ai4r::Clusterers::DBSCAN.new
|
14
|
+
clusterer.set_parameters(epsilon: 10, min_points: 2).build(set)
|
15
|
+
|
16
|
+
pp clusterer.labels
|
17
|
+
pp clusterer.clusters.map(&:data_items)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ai4r'
|
4
|
+
require 'dendrograms'
|
5
|
+
|
6
|
+
points = [[0, 0], [0, 1], [1, 0], [1, 1]]
|
7
|
+
data = Ai4r::Data::DataSet.new(data_items: points)
|
8
|
+
|
9
|
+
clusterer = Ai4r::Clusterers::WardLinkage.new.build(data, 1)
|
10
|
+
|
11
|
+
# Convert stored tree to a simple array of point sets
|
12
|
+
steps = clusterer.cluster_tree.map do |clusters|
|
13
|
+
clusters.map(&:data_items)
|
14
|
+
end
|
15
|
+
|
16
|
+
Dendrograms::Dendrogram.new(steps).draw('dendrogram.png')
|
17
|
+
puts 'Dendrogram saved to dendrogram.png'
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Demonstrates recording the merge tree using WardLinkageHierarchical
|
4
|
+
# and printing a simple dendrogram.
|
5
|
+
|
6
|
+
require 'ai4r'
|
7
|
+
|
8
|
+
points = [[1, 1], [1, 2], [2, 1], [2, 2], [8, 8], [8, 9], [9, 8], [9, 9]]
|
9
|
+
data_set = Ai4r::Data::DataSet.new(data_items: points)
|
10
|
+
|
11
|
+
clusterer = Ai4r::Clusterers::WardLinkageHierarchical.new
|
12
|
+
clusterer.build(data_set, 1)
|
13
|
+
|
14
|
+
puts 'Dendrogram:'
|
15
|
+
clusterer.cluster_tree.each_with_index do |clusters, level|
|
16
|
+
puts "Level #{level}:"
|
17
|
+
clusters.each_with_index do |cluster, idx|
|
18
|
+
puts " Cluster #{idx}: #{cluster.data_items.inspect}"
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This example shows KMeans with a custom distance function and deterministic initialization.
|
4
|
+
# It also prints the number of iterations and the sum of squared errors (SSE).
|
5
|
+
|
6
|
+
require 'ai4r'
|
7
|
+
|
8
|
+
# Simple two-cluster data set
|
9
|
+
points = [[1, 1], [1, 2], [2, 1], [2, 2], [8, 8], [8, 9], [9, 8], [9, 9]]
|
10
|
+
data_set = Ai4r::Data::DataSet.new(data_items: points)
|
11
|
+
|
12
|
+
# Manhattan distance instead of the default squared Euclidean distance
|
13
|
+
manhattan = lambda do |a, b|
|
14
|
+
a.zip(b).map { |x, y| (x - y).abs }.reduce(:+)
|
15
|
+
end
|
16
|
+
|
17
|
+
kmeans = Ai4r::Clusterers::KMeans.new
|
18
|
+
kmeans.set_parameters(distance_function: manhattan, random_seed: 1)
|
19
|
+
.build(data_set, 2)
|
20
|
+
|
21
|
+
kmeans.clusters.each_with_index do |cluster, idx|
|
22
|
+
puts "Cluster #{idx}: #{cluster.data_items.inspect}"
|
23
|
+
end
|
24
|
+
|
25
|
+
puts "Iterations: #{kmeans.iterations}"
|
26
|
+
puts "SSE: #{kmeans.sse}"
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Example using a custom chromosome to maximise ones in a bit string
|
4
|
+
require_relative '../../lib/ai4r/genetic_algorithm/genetic_algorithm'
|
5
|
+
|
6
|
+
# Represents a chromosome consisting of a bit string.
|
7
|
+
class BitStringChromosome < Ai4r::GeneticAlgorithm::ChromosomeBase
|
8
|
+
LENGTH = 16
|
9
|
+
|
10
|
+
def fitness
|
11
|
+
@data.count(1)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.seed
|
15
|
+
new(Array.new(LENGTH) { rand(2) })
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.reproduce(parent_a, parent_b, crossover_rate = 0.4)
|
19
|
+
point = rand(LENGTH)
|
20
|
+
data = parent_a.data[0...point] + parent_b.data[point..]
|
21
|
+
data = parent_b.data[0...point] + parent_a.data[point..] if rand < crossover_rate
|
22
|
+
new(data)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.mutate(chromosome, mutation_rate = 0.3)
|
26
|
+
chromosome.data.map!.with_index do |bit, _|
|
27
|
+
if rand < ((1 - chromosome.normalized_fitness.to_f) * mutation_rate)
|
28
|
+
1 - bit
|
29
|
+
else
|
30
|
+
bit
|
31
|
+
end
|
32
|
+
end
|
33
|
+
chromosome.instance_variable_set(:@fitness, nil)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
search = Ai4r::GeneticAlgorithm::GeneticSearch.new(
|
38
|
+
30, 50, BitStringChromosome, 0.2, 0.7, BitStringChromosome::LENGTH
|
39
|
+
)
|
40
|
+
best = search.run
|
41
|
+
puts "Best fitness #{best.fitness}: #{best.data.join}"
|
@@ -1,37 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Author:: Sergio Fierens
|
2
4
|
# License:: MPL 1.1
|
3
5
|
# Project:: ai4r
|
4
6
|
# Url:: http://www.ai4r.org/
|
5
7
|
#
|
6
|
-
# You can redistribute it and/or modify it under the terms of
|
7
|
-
# the Mozilla Public License version 1.1 as published by the
|
8
|
+
# You can redistribute it and/or modify it under the terms of
|
9
|
+
# the Mozilla Public License version 1.1 as published by the
|
8
10
|
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
+
require_relative '../../lib/ai4r/genetic_algorithm/genetic_algorithm'
|
13
|
+
require_relative '../../lib/ai4r/data/data_set'
|
12
14
|
require 'csv'
|
13
15
|
|
14
16
|
# Load data from data_set.csv
|
15
17
|
data_filename = "#{File.dirname(__FILE__)}/travel_cost.csv"
|
16
18
|
data_set = Ai4r::Data::DataSet.new.load_csv_with_labels data_filename
|
17
|
-
data_set.data_items.collect! {|column| column.collect
|
19
|
+
data_set.data_items.collect! { |column| column.collect(&:to_f) }
|
18
20
|
|
19
|
-
Ai4r::GeneticAlgorithm::
|
21
|
+
Ai4r::GeneticAlgorithm::TspChromosome.set_cost_matrix(data_set.data_items)
|
20
22
|
|
21
|
-
puts
|
23
|
+
puts 'Some random selected tours costs: '
|
22
24
|
3.times do
|
23
|
-
c = Ai4r::GeneticAlgorithm::
|
24
|
-
puts "COST #{-1 * c.fitness} TOUR: "
|
25
|
-
|
25
|
+
c = Ai4r::GeneticAlgorithm::TspChromosome.seed
|
26
|
+
puts "COST #{-1 * c.fitness} TOUR: " \
|
27
|
+
"#{c.data.collect { |c| data_set.data_labels[c] } * ', '}"
|
26
28
|
end
|
27
29
|
|
28
|
-
puts
|
29
|
-
search = Ai4r::GeneticAlgorithm::GeneticSearch.new(
|
30
|
+
puts 'Beginning genetic search, please wait... '
|
31
|
+
search = Ai4r::GeneticAlgorithm::GeneticSearch.new(
|
32
|
+
800, 100, Ai4r::GeneticAlgorithm::TspChromosome,
|
33
|
+
0.3, 0.4, nil, nil,
|
34
|
+
->(generation, best) { puts "Generation #{generation}: best fitness #{best}" }
|
35
|
+
)
|
30
36
|
result = search.run
|
31
|
-
puts "COST #{-1 * result.fitness} TOUR: "
|
32
|
-
|
37
|
+
puts "COST #{-1 * result.fitness} TOUR: " \
|
38
|
+
"#{result.data.collect { |c| data_set.data_labels[c] } * ', '}"
|
33
39
|
|
34
|
-
# $7611.99 TOUR: Moscow, Kiev, Warsaw, Hamburg, Berlin, Vienna, Munich, Milan,
|
35
|
-
#
|
36
|
-
# $
|
37
|
-
|
40
|
+
# $7611.99 TOUR: Moscow, Kiev, Warsaw, Hamburg, Berlin, Vienna, Munich, Milan,
|
41
|
+
# Rome, Barcelona, Madrid, Paris, Brussels, London, Dublin
|
42
|
+
# $7659.81 TOUR: Moscow, Kiev, Warsaw, Vienna, Munich, Berlin, Hamburg, Brussels,
|
43
|
+
# Dublin, London, Paris, Milan, Rome, Barcelona, Madrid
|
44
|
+
# $7596.74 TOUR: Moscow, Kiev, Warsaw, Berlin, Hamburg, Vienna, Munich, Milan,
|
45
|
+
# Rome, Barcelona, Madrid, Paris, Brussels, London Dublin
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative '../../lib/ai4r/genetic_algorithm/genetic_algorithm'
|
5
|
+
require_relative '../../lib/ai4r/clusterers/k_means'
|
6
|
+
require_relative '../som/som_data'
|
7
|
+
|
8
|
+
##
|
9
|
+
# Running the genetic search without a fixed random seed leads to
|
10
|
+
# different SSE results each time, which causes flaky tests in CI.
|
11
|
+
# Explicitly seed Ruby's random number generator so that the script
|
12
|
+
# behaves deterministically across runs.
|
13
|
+
srand 1
|
14
|
+
|
15
|
+
# Chromosome used to search for a good random seed for KMeans.
|
16
|
+
class SeedChromosome < Ai4r::GeneticAlgorithm::ChromosomeBase
|
17
|
+
RANGE = (0..10)
|
18
|
+
DATA = Ai4r::Data::DataSet.new(data_items: SOM_DATA.first(30))
|
19
|
+
|
20
|
+
def fitness
|
21
|
+
return @fitness if @fitness
|
22
|
+
|
23
|
+
kmeans = Ai4r::Clusterers::KMeans.new.set_parameters(random_seed: @data).build(DATA, 3)
|
24
|
+
@fitness = -kmeans.sse
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.seed
|
28
|
+
new(RANGE.to_a.sample)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.reproduce(parent_a, parent_b, _rate = 0.7)
|
32
|
+
new([parent_a.data, parent_b.data].sample)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.mutate(chrom, rate = 0.3)
|
36
|
+
return unless rand < rate
|
37
|
+
|
38
|
+
chrom.data = RANGE.to_a.sample
|
39
|
+
chrom.instance_variable_set(:@fitness, nil)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
search = Ai4r::GeneticAlgorithm::GeneticSearch.new(6, 5, SeedChromosome, 0.1, 0.7)
|
44
|
+
best = search.run
|
45
|
+
puts(-best.fitness)
|
@@ -1,67 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Author:: Sergio Fierens
|
2
4
|
# License:: MPL 1.1
|
3
5
|
# Project:: ai4r
|
4
6
|
# Url:: http://www.ai4r.org/
|
5
7
|
#
|
6
|
-
# You can redistribute it and/or modify it under the terms of
|
7
|
-
# the Mozilla Public License version 1.1 as published by the
|
8
|
+
# You can redistribute it and/or modify it under the terms of
|
9
|
+
# the Mozilla Public License version 1.1 as published by the
|
8
10
|
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
require_relative 'training_patterns'
|
13
|
+
require_relative 'patterns_with_noise'
|
14
|
+
require_relative 'patterns_with_base_noise'
|
15
|
+
require_relative '../../lib/ai4r/neural_network/backpropagation'
|
14
16
|
require 'benchmark'
|
15
17
|
|
16
18
|
times = Benchmark.measure do
|
17
|
-
|
18
|
-
srand 1
|
19
|
-
|
20
|
-
net = Ai4r::NeuralNetwork::Backpropagation.new([256, 3])
|
21
|
-
|
22
|
-
tr_input = TRIANGLE.flatten.collect { |input| input.to_f / 5.0}
|
23
|
-
sq_input = SQUARE.flatten.collect { |input| input.to_f / 5.0}
|
24
|
-
cr_input = CROSS.flatten.collect { |input| input.to_f / 5.0}
|
19
|
+
srand 1
|
25
20
|
|
26
|
-
|
27
|
-
|
28
|
-
cr_with_noise = CROSS_WITH_NOISE.flatten.collect { |input| input.to_f / 5.0}
|
21
|
+
# creating network with 256 input-neurons, 3-neurons and 0 hidden layers
|
22
|
+
net = Ai4r::NeuralNetwork::Backpropagation.new([256, 3])
|
29
23
|
|
30
|
-
|
31
|
-
|
32
|
-
|
24
|
+
tr_input = TRIANGLE.flatten.collect { |input| input.to_f / 5.0 }
|
25
|
+
sq_input = SQUARE.flatten.collect { |input| input.to_f / 5.0 }
|
26
|
+
cr_input = CROSS.flatten.collect { |input| input.to_f / 5.0 }
|
33
27
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
28
|
+
tr_with_noise = TRIANGLE_WITH_NOISE.flatten.collect { |input| input.to_f / 5.0 }
|
29
|
+
sq_with_noise = SQUARE_WITH_NOISE.flatten.collect { |input| input.to_f / 5.0 }
|
30
|
+
cr_with_noise = CROSS_WITH_NOISE.flatten.collect { |input| input.to_f / 5.0 }
|
31
|
+
|
32
|
+
tr_with_base_noise = TRIANGLE_WITH_BASE_NOISE.flatten.collect { |input| input.to_f / 5.0 }
|
33
|
+
sq_with_base_noise = SQUARE_WITH_BASE_NOISE.flatten.collect { |input| input.to_f / 5.0 }
|
34
|
+
cr_with_base_noise = CROSS_WITH_BASE_NOISE.flatten.collect { |input| input.to_f / 5.0 }
|
35
|
+
|
36
|
+
puts 'Training the network, please wait.'
|
37
|
+
101.times do |i|
|
38
|
+
net.train(tr_input, [1, 0, 0])
|
39
|
+
net.train(sq_input, [0, 1, 0])
|
40
|
+
error = net.train(cr_input, [0, 0, 1])
|
41
|
+
puts "Error after iteration #{i}:\t#{error}" if (i % 20).zero?
|
42
|
+
end
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
end
|
44
|
+
def result_label(result)
|
45
|
+
if result[0] > result[1] && result[0] > result[2]
|
46
|
+
'TRIANGLE'
|
47
|
+
elsif result[1] > result[2]
|
48
|
+
'SQUARE'
|
49
|
+
else
|
50
|
+
'CROSS'
|
50
51
|
end
|
52
|
+
end
|
51
53
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
54
|
+
puts 'Training Examples'
|
55
|
+
puts "#{net.eval(tr_input).inspect} => #{result_label(net.eval(tr_input))}"
|
56
|
+
puts "#{net.eval(sq_input).inspect} => #{result_label(net.eval(sq_input))}"
|
57
|
+
puts "#{net.eval(cr_input).inspect} => #{result_label(net.eval(cr_input))}"
|
58
|
+
puts 'Examples with noise'
|
59
|
+
puts "#{net.eval(tr_with_noise).inspect} => #{result_label(net.eval(tr_with_noise))}"
|
60
|
+
puts "#{net.eval(sq_with_noise).inspect} => #{result_label(net.eval(sq_with_noise))}"
|
61
|
+
puts "#{net.eval(cr_with_noise).inspect} => #{result_label(net.eval(cr_with_noise))}"
|
62
|
+
puts 'Examples with base noise'
|
63
|
+
puts "#{net.eval(tr_with_base_noise).inspect} => #{result_label(net.eval(tr_with_base_noise))}"
|
64
|
+
puts "#{net.eval(sq_with_base_noise).inspect} => #{result_label(net.eval(sq_with_base_noise))}"
|
65
|
+
puts "#{net.eval(cr_with_base_noise).inspect} => #{result_label(net.eval(cr_with_base_noise))}"
|
65
66
|
end
|
66
67
|
|
67
68
|
puts "Elapsed time: #{times}"
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Author:: Sergio Fierens
|
4
|
+
# License:: MPL 1.1
|
5
|
+
# Project:: ai4r
|
6
|
+
# Url:: http://www.ai4r.org/
|
7
|
+
#
|
8
|
+
# You can redistribute it and/or modify it under the terms of
|
9
|
+
# the Mozilla Public License version 1.1 as published by the
|
10
|
+
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
11
|
+
|
12
|
+
require_relative '../../lib/ai4r/neural_network/hopfield'
|
13
|
+
require_relative '../../lib/ai4r/data/data_set'
|
14
|
+
|
15
|
+
patterns = [
|
16
|
+
[1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1],
|
17
|
+
[-1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1],
|
18
|
+
[-1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1],
|
19
|
+
[1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1]
|
20
|
+
]
|
21
|
+
|
22
|
+
noisy_patterns = [
|
23
|
+
[1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1],
|
24
|
+
[-1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1, 1, 1],
|
25
|
+
[-1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1],
|
26
|
+
[-1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, -1]
|
27
|
+
]
|
28
|
+
|
29
|
+
data = Ai4r::Data::DataSet.new(data_items: patterns)
|
30
|
+
|
31
|
+
# Use random asynchronous updates instead of the default sequential
|
32
|
+
# strategy. Random updates are closer to the original Hopfield
|
33
|
+
# formulation and may help the network escape shallow local minima.
|
34
|
+
net = Ai4r::NeuralNetwork::Hopfield.new
|
35
|
+
net.set_parameters(update_strategy: :async_random)
|
36
|
+
net.train(data)
|
37
|
+
|
38
|
+
puts 'Evaluation of noisy patterns:'
|
39
|
+
noisy_patterns.each do |p|
|
40
|
+
# Pass `trace: true` to record the energy of each iteration so we can
|
41
|
+
# inspect how the network converges to a memorized pattern.
|
42
|
+
trace = net.eval(p, trace: true)
|
43
|
+
puts "#{p.inspect} => #{trace[:states].last.inspect}"
|
44
|
+
puts "Energy trace: #{trace[:energies].inspect}"
|
45
|
+
end
|
@@ -1,31 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Author:: Sergio Fierens
|
2
4
|
# License:: MPL 1.1
|
3
5
|
# Project:: ai4r
|
4
6
|
# Url:: http://www.ai4r.org/
|
5
7
|
#
|
6
|
-
# You can redistribute it and/or modify it under the terms of
|
7
|
-
# the Mozilla Public License version 1.1 as published by the
|
8
|
+
# You can redistribute it and/or modify it under the terms of
|
9
|
+
# the Mozilla Public License version 1.1 as published by the
|
8
10
|
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
9
|
-
|
10
11
|
|
11
12
|
TRIANGLE_WITH_BASE_NOISE = [
|
12
|
-
[
|
13
|
-
[
|
14
|
-
[
|
15
|
-
[
|
16
|
-
[
|
17
|
-
[
|
18
|
-
[
|
19
|
-
[
|
20
|
-
[
|
21
|
-
[
|
22
|
-
[
|
23
|
-
[
|
24
|
-
[
|
25
|
-
[
|
26
|
-
[
|
13
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3],
|
14
|
+
[3, 3, 3, 3, 3, 3, 4, 10, 10, 4, 3, 3, 3, 3, 3, 3],
|
15
|
+
[3, 3, 3, 3, 3, 3, 8, 8, 8, 8, 3, 3, 3, 3, 3, 3],
|
16
|
+
[3, 3, 3, 3, 3, 4, 10, 4, 4, 10, 4, 3, 3, 3, 3, 3],
|
17
|
+
[3, 3, 3, 3, 3, 8, 8, 3, 3, 8, 8, 3, 3, 3, 3, 3],
|
18
|
+
[3, 3, 3, 3, 4, 10, 4, 3, 3, 4, 10, 4, 3, 3, 3, 3],
|
19
|
+
[3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3],
|
20
|
+
[3, 3, 3, 4, 10, 4, 3, 3, 3, 3, 4, 10, 4, 3, 3, 3],
|
21
|
+
[3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3],
|
22
|
+
[3, 3, 4, 10, 4, 3, 3, 3, 3, 3, 3, 4, 10, 4, 3, 3],
|
23
|
+
[3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3],
|
24
|
+
[3, 4, 10, 4, 3, 3, 3, 3, 3, 3, 3, 3, 4, 10, 4, 3],
|
25
|
+
[3, 8, 8, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 8, 8, 3],
|
26
|
+
[4, 10, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 10, 4],
|
27
|
+
[8, 8, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 8, 8],
|
27
28
|
[10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
|
28
|
-
]
|
29
|
+
].freeze
|
29
30
|
|
30
31
|
SQUARE_WITH_BASE_NOISE = [
|
31
32
|
[10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10],
|
@@ -44,25 +45,24 @@ SQUARE_WITH_BASE_NOISE = [
|
|
44
45
|
[10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 10],
|
45
46
|
[10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 10],
|
46
47
|
[10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
|
47
|
-
|
48
|
-
]
|
48
|
+
|
49
|
+
].freeze
|
49
50
|
|
50
51
|
CROSS_WITH_BASE_NOISE = [
|
51
|
-
[
|
52
|
-
[
|
53
|
-
[
|
54
|
-
[
|
55
|
-
[
|
56
|
-
[
|
57
|
-
[
|
58
|
-
[
|
59
|
-
[
|
60
|
-
[
|
61
|
-
[
|
62
|
-
[
|
63
|
-
[
|
64
|
-
[
|
65
|
-
[
|
66
|
-
[
|
67
|
-
]
|
68
|
-
|
52
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3],
|
53
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3],
|
54
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3],
|
55
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3],
|
56
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3],
|
57
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3],
|
58
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3],
|
59
|
+
[8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8],
|
60
|
+
[8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8],
|
61
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3],
|
62
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3],
|
63
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3],
|
64
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3],
|
65
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3],
|
66
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3],
|
67
|
+
[3, 3, 3, 3, 3, 3, 3, 8, 8, 3, 3, 3, 3, 3, 3, 3]
|
68
|
+
].freeze
|