ai4ruby 1.11
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.
- data/README.rdoc +47 -0
- data/examples/classifiers/id3_data.csv +121 -0
- data/examples/classifiers/id3_example.rb +29 -0
- data/examples/classifiers/naive_bayes_data.csv +11 -0
- data/examples/classifiers/naive_bayes_example.rb +16 -0
- data/examples/classifiers/results.txt +31 -0
- data/examples/genetic_algorithm/genetic_algorithm_example.rb +37 -0
- data/examples/genetic_algorithm/travel_cost.csv +16 -0
- data/examples/neural_network/backpropagation_example.rb +67 -0
- data/examples/neural_network/patterns_with_base_noise.rb +68 -0
- data/examples/neural_network/patterns_with_noise.rb +66 -0
- data/examples/neural_network/training_patterns.rb +68 -0
- data/examples/neural_network/xor_example.rb +35 -0
- data/examples/som/som_data.rb +156 -0
- data/examples/som/som_multi_node_example.rb +22 -0
- data/examples/som/som_single_example.rb +24 -0
- data/lib/ai4r.rb +33 -0
- data/lib/ai4r/classifiers/classifier.rb +62 -0
- data/lib/ai4r/classifiers/hyperpipes.rb +118 -0
- data/lib/ai4r/classifiers/ib1.rb +121 -0
- data/lib/ai4r/classifiers/id3.rb +326 -0
- data/lib/ai4r/classifiers/multilayer_perceptron.rb +135 -0
- data/lib/ai4r/classifiers/naive_bayes.rb +259 -0
- data/lib/ai4r/classifiers/one_r.rb +110 -0
- data/lib/ai4r/classifiers/prism.rb +197 -0
- data/lib/ai4r/classifiers/zero_r.rb +73 -0
- data/lib/ai4r/clusterers/average_linkage.rb +59 -0
- data/lib/ai4r/clusterers/bisecting_k_means.rb +93 -0
- data/lib/ai4r/clusterers/centroid_linkage.rb +66 -0
- data/lib/ai4r/clusterers/clusterer.rb +61 -0
- data/lib/ai4r/clusterers/complete_linkage.rb +67 -0
- data/lib/ai4r/clusterers/diana.rb +139 -0
- data/lib/ai4r/clusterers/k_means.rb +126 -0
- data/lib/ai4r/clusterers/median_linkage.rb +61 -0
- data/lib/ai4r/clusterers/single_linkage.rb +194 -0
- data/lib/ai4r/clusterers/ward_linkage.rb +64 -0
- data/lib/ai4r/clusterers/ward_linkage_hierarchical.rb +31 -0
- data/lib/ai4r/clusterers/weighted_average_linkage.rb +61 -0
- data/lib/ai4r/data/data_set.rb +266 -0
- data/lib/ai4r/data/parameterizable.rb +64 -0
- data/lib/ai4r/data/proximity.rb +100 -0
- data/lib/ai4r/data/statistics.rb +77 -0
- data/lib/ai4r/experiment/classifier_evaluator.rb +95 -0
- data/lib/ai4r/genetic_algorithm/genetic_algorithm.rb +270 -0
- data/lib/ai4r/neural_network/backpropagation.rb +326 -0
- data/lib/ai4r/neural_network/hopfield.rb +149 -0
- data/lib/ai4r/som/layer.rb +68 -0
- data/lib/ai4r/som/node.rb +96 -0
- data/lib/ai4r/som/som.rb +155 -0
- data/lib/ai4r/som/two_phase_layer.rb +90 -0
- data/test/classifiers/hyperpipes_test.rb +84 -0
- data/test/classifiers/ib1_test.rb +78 -0
- data/test/classifiers/id3_test.rb +208 -0
- data/test/classifiers/multilayer_perceptron_test.rb +79 -0
- data/test/classifiers/naive_bayes_test.rb +43 -0
- data/test/classifiers/one_r_test.rb +62 -0
- data/test/classifiers/prism_test.rb +85 -0
- data/test/classifiers/zero_r_test.rb +49 -0
- data/test/clusterers/average_linkage_test.rb +51 -0
- data/test/clusterers/bisecting_k_means_test.rb +66 -0
- data/test/clusterers/centroid_linkage_test.rb +53 -0
- data/test/clusterers/complete_linkage_test.rb +57 -0
- data/test/clusterers/diana_test.rb +69 -0
- data/test/clusterers/k_means_test.rb +100 -0
- data/test/clusterers/median_linkage_test.rb +53 -0
- data/test/clusterers/single_linkage_test.rb +122 -0
- data/test/clusterers/ward_linkage_hierarchical_test.rb +61 -0
- data/test/clusterers/ward_linkage_test.rb +53 -0
- data/test/clusterers/weighted_average_linkage_test.rb +53 -0
- data/test/data/data_set_test.rb +96 -0
- data/test/data/proximity_test.rb +81 -0
- data/test/data/statistics_test.rb +65 -0
- data/test/experiment/classifier_evaluator_test.rb +76 -0
- data/test/genetic_algorithm/chromosome_test.rb +58 -0
- data/test/genetic_algorithm/genetic_algorithm_test.rb +81 -0
- data/test/neural_network/backpropagation_test.rb +82 -0
- data/test/neural_network/hopfield_test.rb +72 -0
- data/test/som/som_test.rb +97 -0
- metadata +168 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
# Author:: Thomas Kern
|
2
|
+
# License:: MPL 1.1
|
3
|
+
# Project:: ai4r
|
4
|
+
# Url:: http://ai4r.rubyforge.org/
|
5
|
+
#
|
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
|
+
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
9
|
+
|
10
|
+
require File.dirname(__FILE__) + '/../data/parameterizable'
|
11
|
+
|
12
|
+
module Ai4r
|
13
|
+
|
14
|
+
module Som
|
15
|
+
|
16
|
+
# responsible for the implementation of the algorithm's decays
|
17
|
+
# currently has methods for the decay of the radius, influence and learning rate.
|
18
|
+
# Has only one phase, which ends after the number of epochs is passed by the Som-class.
|
19
|
+
#
|
20
|
+
# = Parameters
|
21
|
+
# * nodes => number of nodes in the SOM (nodes x nodes). Has to be the same number
|
22
|
+
# you pass to the SOM. Has to be an integer
|
23
|
+
# * radius => the initial radius for the neighborhood
|
24
|
+
# * epochs => number of epochs the algorithm runs, has to be an integer. By default it is set to 100
|
25
|
+
# * learning_rate => sets the initial learning rate
|
26
|
+
class Layer
|
27
|
+
|
28
|
+
include Ai4r::Data::Parameterizable
|
29
|
+
|
30
|
+
parameters_info :nodes => "number of nodes, has to be equal to the som",
|
31
|
+
:epochs => "number of epochs the algorithm has to run",
|
32
|
+
:radius => "sets the initial neighborhoud radius"
|
33
|
+
|
34
|
+
def initialize(nodes, radius, epochs = 100, learning_rate = 0.7)
|
35
|
+
raise("Too few nodes") if nodes < 3
|
36
|
+
|
37
|
+
@nodes = nodes
|
38
|
+
@epochs = epochs
|
39
|
+
@radius = radius
|
40
|
+
@time_for_epoch = @epochs / Math.log(nodes / 4.0)
|
41
|
+
@time_for_epoch = @epochs + 1.0 if @time_for_epoch < @epochs
|
42
|
+
|
43
|
+
@initial_learning_rate = learning_rate
|
44
|
+
end
|
45
|
+
|
46
|
+
# calculates the influnce decay for a certain distance and the current radius
|
47
|
+
# of the epoch
|
48
|
+
def influence_decay(distance, radius)
|
49
|
+
Math.exp(- (distance.to_f**2 / 2.0 / radius.to_f**2))
|
50
|
+
end
|
51
|
+
|
52
|
+
# calculates the radius decay for the current epoch. Uses @time_for_epoch
|
53
|
+
# which has to be higher than the number of epochs, otherwise the decay will be - Infinity
|
54
|
+
def radius_decay(epoch)
|
55
|
+
(@radius * ( 1 - epoch/ @time_for_epoch)).round
|
56
|
+
end
|
57
|
+
|
58
|
+
# calculates the learning rate decay. uses @time_for_epoch again and same rule applies:
|
59
|
+
# @time_for_epoch has to be higher than the number of epochs, otherwise the decay will be - Infinity
|
60
|
+
def learning_rate_decay(epoch)
|
61
|
+
@initial_learning_rate * ( 1 - epoch / @time_for_epoch)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# Author:: Thomas Kern
|
2
|
+
# License:: MPL 1.1
|
3
|
+
# Project:: ai4r
|
4
|
+
# Url:: http://ai4r.rubyforge.org/
|
5
|
+
#
|
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
|
+
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
9
|
+
|
10
|
+
require File.dirname(__FILE__) + '/../data/parameterizable'
|
11
|
+
require File.dirname(__FILE__) + '/layer'
|
12
|
+
|
13
|
+
module Ai4r
|
14
|
+
|
15
|
+
module Som
|
16
|
+
|
17
|
+
# this class is used for the individual node and will be (nodes * nodes)-time instantiated
|
18
|
+
#
|
19
|
+
# = attributes
|
20
|
+
#
|
21
|
+
# * direct access to the x and y values is granted, those show the position of the node in
|
22
|
+
# the square map
|
23
|
+
# * id => is the uniq and sequential ID of the node
|
24
|
+
# * weights => values of the current weights are stored in an array of dimension 'dimensions'.
|
25
|
+
# Weights are of type float
|
26
|
+
# * instantiated_weight => the values of the first instantiation of weights. these values are
|
27
|
+
# never changed
|
28
|
+
|
29
|
+
class Node
|
30
|
+
|
31
|
+
include Ai4r::Data::Parameterizable
|
32
|
+
|
33
|
+
parameters_info :weights => "holds the current weight",
|
34
|
+
:instantiated_weight => "holds the very first weight",
|
35
|
+
:x => "holds the row ID of the unit in the map",
|
36
|
+
:y => "holds the column ID of the unit in the map",
|
37
|
+
:id => "id of the node"
|
38
|
+
|
39
|
+
# creates an instance of Node and instantiates the weights
|
40
|
+
# the parameters is a uniq and sequential ID as well as the number of total nodes
|
41
|
+
# dimensions signals the dimension of the input vector
|
42
|
+
def self.create(id, total, dimensions)
|
43
|
+
n = Node.new
|
44
|
+
n.id = id
|
45
|
+
n.instantiate_weight dimensions
|
46
|
+
n.x = id % total
|
47
|
+
n.y = (id / total.to_f).to_i
|
48
|
+
n
|
49
|
+
end
|
50
|
+
|
51
|
+
# instantiates the weights to the dimension (of the input vector)
|
52
|
+
# for backup reasons, the instantiated weight is stored into @instantiated_weight as well
|
53
|
+
def instantiate_weight(dimensions)
|
54
|
+
@weights = Array.new dimensions
|
55
|
+
@instantiated_weight = Array.new dimensions
|
56
|
+
@weights.each_with_index do |weight, index|
|
57
|
+
@weights[index] = rand
|
58
|
+
@instantiated_weight[index] = @weights[index]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# returns the square distance between the current weights and the input
|
63
|
+
# the input is a vector/array of the same size as weights
|
64
|
+
# at the end, the square root is extracted from the sum of differences
|
65
|
+
def distance_to_input(input)
|
66
|
+
dist = 0
|
67
|
+
input.each_with_index do |i, index|
|
68
|
+
dist += (i - @weights[index]) ** 2
|
69
|
+
end
|
70
|
+
|
71
|
+
Math.sqrt(dist)
|
72
|
+
end
|
73
|
+
|
74
|
+
# returns the distance in square-form from the instance node to the passed node
|
75
|
+
# example:
|
76
|
+
# 2 2 2 2 2
|
77
|
+
# 2 1 1 1 2
|
78
|
+
# 2 1 0 1 2
|
79
|
+
# 2 1 1 1 2
|
80
|
+
# 2 2 2 2 2
|
81
|
+
# 0 being the current node
|
82
|
+
def distance_to_node(node)
|
83
|
+
max((self.x - node.x).abs, (self.y - node.y).abs)
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def max(a, b)
|
89
|
+
a > b ? a : b
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
data/lib/ai4r/som/som.rb
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
# Author:: Thomas Kern
|
2
|
+
# License:: MPL 1.1
|
3
|
+
# Project:: ai4r
|
4
|
+
# Url:: http://ai4r.rubyforge.org/
|
5
|
+
#
|
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
|
+
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
9
|
+
|
10
|
+
require File.dirname(__FILE__) + '/../data/parameterizable'
|
11
|
+
require File.dirname(__FILE__) + '/layer'
|
12
|
+
require File.dirname(__FILE__) + '/two_phase_layer'
|
13
|
+
require File.dirname(__FILE__) + '/node'
|
14
|
+
|
15
|
+
module Ai4r
|
16
|
+
|
17
|
+
# A self-organizing map (SOM) or self-organizing feature map (SOFM) is a type
|
18
|
+
# of artificial neural network that is trained using unsupervised learning to
|
19
|
+
# produce a low-dimensional (typically two-dimensional), discretized
|
20
|
+
# representation of the input space of the training samples, called a map.
|
21
|
+
|
22
|
+
# for more have a look at http://en.wikipedia.org/wiki/Self-organizing_map
|
23
|
+
# an in-depth explanation is provided by Sandhya Samarasinghe in
|
24
|
+
# 'Neural Networks for Applied Sciences and Engineering'
|
25
|
+
|
26
|
+
module Som
|
27
|
+
|
28
|
+
# = Introduction
|
29
|
+
#
|
30
|
+
# This is an implementation of a Kohonen Self-Organizing Maps
|
31
|
+
#
|
32
|
+
# = Features
|
33
|
+
#
|
34
|
+
# * Support for any network architecture (number of layers and neurons)
|
35
|
+
# * Configurable propagation function
|
36
|
+
# * Optional usage of bias
|
37
|
+
# * Configurable momentum
|
38
|
+
# * Configurable learning rate
|
39
|
+
# * Configurable initial weight function
|
40
|
+
# * 100% ruby code, no external dependency
|
41
|
+
#
|
42
|
+
# = Parameters
|
43
|
+
# * dim => dimension of the input vector
|
44
|
+
# * number_of_nodes => is the number of nodes per row/column (square som).
|
45
|
+
# * layer => instante of a layer-algorithm class
|
46
|
+
#
|
47
|
+
# = About the project
|
48
|
+
# Author:: Thomas Kern
|
49
|
+
# License:: MPL 1.1
|
50
|
+
# Url:: http://ai4r.rubyforge.org
|
51
|
+
|
52
|
+
class Som
|
53
|
+
|
54
|
+
include Ai4r::Data::Parameterizable
|
55
|
+
|
56
|
+
parameters_info :nodes => "sets the architecture of the map (nodes x nodes)",
|
57
|
+
:dimension => "sets the dimension of the input",
|
58
|
+
:layer => "instance of a layer, defines how the training algorithm works",
|
59
|
+
:epoch => "number of finished epochs"
|
60
|
+
|
61
|
+
def initialize(dim, number_of_nodes, layer)
|
62
|
+
@layer = layer
|
63
|
+
@dimension = dim
|
64
|
+
@number_of_nodes = number_of_nodes
|
65
|
+
@nodes = Array.new(number_of_nodes * number_of_nodes)
|
66
|
+
@epoch = 0
|
67
|
+
@cache = {}
|
68
|
+
end
|
69
|
+
|
70
|
+
# finds the best matching unit (bmu) of a certain input in all the @nodes
|
71
|
+
# returns an array of length 2 => [node, distance] (distance is of eucledian type, not
|
72
|
+
# a neighborhood distance)
|
73
|
+
def find_bmu(input)
|
74
|
+
bmu = @nodes.first
|
75
|
+
dist = bmu.distance_to_input input
|
76
|
+
@nodes[1..-1].each do |node|
|
77
|
+
tmp_dist = node.distance_to_input(input)
|
78
|
+
if tmp_dist <= dist
|
79
|
+
dist = tmp_dist
|
80
|
+
bmu = node
|
81
|
+
end
|
82
|
+
end
|
83
|
+
[bmu, dist]
|
84
|
+
end
|
85
|
+
|
86
|
+
# adjusts all nodes within a certain radius to the bmu
|
87
|
+
def adjust_nodes(input, bmu, radius, learning_rate)
|
88
|
+
@nodes.each do |node|
|
89
|
+
dist = node.distance_to_node(bmu[0])
|
90
|
+
next unless dist < radius
|
91
|
+
|
92
|
+
influence = @layer.influence_decay dist, radius
|
93
|
+
node.weights.each_with_index do |weight, index|
|
94
|
+
node.weights[index] += influence * learning_rate * (input[index] - weight)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# main method for the som. trains the map with the passed data vector
|
100
|
+
# calls train_step as long as train_step returns false
|
101
|
+
def train(data)
|
102
|
+
while !train_step(data)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# calculates the global distance error for all data entries
|
107
|
+
def global_error(data)
|
108
|
+
data.inject(0) {|sum,entry| sum + find_bmu(entry)[1]**2 }
|
109
|
+
end
|
110
|
+
|
111
|
+
# trains the map with the data as long as the @epoch is smaller than the epoch-value of
|
112
|
+
# @layer
|
113
|
+
# returns true if @epoch is greater than the fixed epoch-value in @layer, otherwise false
|
114
|
+
# 1 is added to @epoch at each method call
|
115
|
+
# the radius and learning rate is decreased at each method call/epoch as well
|
116
|
+
def train_step(data)
|
117
|
+
return true if @epoch >= @layer.epochs
|
118
|
+
|
119
|
+
radius = @layer.radius_decay @epoch
|
120
|
+
learning_rate = @layer.learning_rate_decay @epoch
|
121
|
+
|
122
|
+
data.each do |entry|
|
123
|
+
adjust_nodes entry, find_bmu(entry), radius, learning_rate
|
124
|
+
end
|
125
|
+
|
126
|
+
@epoch += 1
|
127
|
+
false
|
128
|
+
end
|
129
|
+
|
130
|
+
# returns the node at position (x,y) in the square map
|
131
|
+
def get_node(x, y)
|
132
|
+
raise(Exception.new) if check_param_for_som(x,y)
|
133
|
+
@nodes[y + x * @number_of_nodes]
|
134
|
+
end
|
135
|
+
|
136
|
+
# intitiates the map by creating (@number_of_nodes * @number_of_nodes) nodes
|
137
|
+
def initiate_map
|
138
|
+
@nodes.each_with_index do |node, i|
|
139
|
+
@nodes[i] = Node.create i, @number_of_nodes, @dimension
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
|
145
|
+
# checks whether or not there is a node in the map at the coordinates (x,y).
|
146
|
+
# x is the row, y the column indicator
|
147
|
+
def check_param_for_som(x, y)
|
148
|
+
y > @number_of_nodes - 1 || x > @number_of_nodes - 1 || x < 0 || y < 0
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# Author:: Thomas Kern
|
2
|
+
# License:: MPL 1.1
|
3
|
+
# Project:: ai4r
|
4
|
+
# Url:: http://ai4r.rubyforge.org/
|
5
|
+
#
|
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
|
+
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
9
|
+
|
10
|
+
require File.dirname(__FILE__) + '/../data/parameterizable'
|
11
|
+
require File.dirname(__FILE__) + '/layer'
|
12
|
+
|
13
|
+
module Ai4r
|
14
|
+
|
15
|
+
module Som
|
16
|
+
|
17
|
+
# responsible for the implementation of the algorithm's decays, extends the class Layer.
|
18
|
+
# currently overrides the radius and learning rate decay methods of Layer.
|
19
|
+
# Has two phases, phase one has a decay in both the learning rate and the radius. The number
|
20
|
+
# of epochs for both phases can be passed and the total number of epochs is the sum of epoch
|
21
|
+
# for phase one and phase two.
|
22
|
+
# In the scond phase, the learning and radius decay is steady, normally set to a small number (ie. 0.01)
|
23
|
+
#
|
24
|
+
# = Parameters
|
25
|
+
# * nodes => number of nodes in the SOM (nodes x nodes). Has to be the same number
|
26
|
+
# you pass to the SOM. Has to be an integer
|
27
|
+
# * radius => the initial radius for the neighborhood
|
28
|
+
# * phase_one => number of epochs for phase one, has to be an integer. By default it is set to 150
|
29
|
+
# * phase_two => number of epochs for phase two, has to be an integer. By default it is set to 100
|
30
|
+
# * learning_rate => sets the initial learning rate
|
31
|
+
# * phase_one_learning_rate => sets the learning rate for phase one
|
32
|
+
# * phase_two_learning_rate => sets the learning rate for phase two
|
33
|
+
|
34
|
+
class TwoPhaseLayer < Layer
|
35
|
+
|
36
|
+
def initialize(nodes, learning_rate = 0.9, phase_one = 150, phase_two = 100,
|
37
|
+
phase_one_learning_rate = 0.1, phase_two_learning_rate = 0)
|
38
|
+
super nodes, nodes, phase_one + phase_two, learning_rate
|
39
|
+
@phase_one = phase_one
|
40
|
+
@phase_two = phase_two
|
41
|
+
@lr = @initial_learning_rate
|
42
|
+
|
43
|
+
@phase_one_learning_rate = phase_one_learning_rate
|
44
|
+
@phase_two_learning_rate = phase_two_learning_rate
|
45
|
+
|
46
|
+
@radius_reduction = @phase_one / (nodes/2.0 - 1) + 1
|
47
|
+
@delta_lr = (@lr - @phase_one_learning_rate)/ @phase_one
|
48
|
+
@radius = (nodes / 2.0).to_i
|
49
|
+
end
|
50
|
+
|
51
|
+
# two different values will be returned, depending on the phase
|
52
|
+
# in phase one, the radius will incrementially reduced by 1 every @radius_reduction time
|
53
|
+
# in phase two, the radius is fixed to 1
|
54
|
+
def radius_decay(epoch)
|
55
|
+
if epoch > @phase_one
|
56
|
+
return 1
|
57
|
+
else
|
58
|
+
if (epoch % @radius_reduction) == 0
|
59
|
+
@radius -= 1
|
60
|
+
end
|
61
|
+
@radius
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
# two different values will be returned, depending on the phase
|
67
|
+
# in phase one, the rate will incrementially reduced everytime this method is called
|
68
|
+
# on the switch of phases, the learning rate will be reset and the delta_lr (which signals
|
69
|
+
# the decay value of the learning rate) is reset as well
|
70
|
+
# in phase two, the newly reset delta_lr rate will be used to incrementially reduce the
|
71
|
+
# learning rate
|
72
|
+
def learning_rate_decay(epoch)
|
73
|
+
if epoch < @phase_one
|
74
|
+
@lr -= @delta_lr
|
75
|
+
return @lr
|
76
|
+
elsif epoch == @phase_one
|
77
|
+
@lr = @phase_one_learning_rate
|
78
|
+
@delta_lr = (@phase_one_learning_rate - @phase_two_learning_rate)/@phase_two
|
79
|
+
return @lr
|
80
|
+
else
|
81
|
+
@lr -= @delta_lr
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Author:: Sergio Fierens
|
2
|
+
# License:: MPL 1.1
|
3
|
+
# Project:: ai4r
|
4
|
+
# Url:: http://ai4r.rubyforge.org/
|
5
|
+
#
|
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
|
+
# Mozilla Foundation at http://www.mozilla.org/MPL/MPL-1.1.txt
|
9
|
+
|
10
|
+
require File.dirname(__FILE__) + '/../../lib/ai4r/classifiers/hyperpipes'
|
11
|
+
require 'test/unit'
|
12
|
+
|
13
|
+
class Ai4r::Classifiers::Hyperpipes
|
14
|
+
attr_accessor :data_set, :pipes
|
15
|
+
end
|
16
|
+
|
17
|
+
include Ai4r::Classifiers
|
18
|
+
include Ai4r::Data
|
19
|
+
|
20
|
+
class HyperpipesTest < Test::Unit::TestCase
|
21
|
+
|
22
|
+
@@data_labels = [ 'city', 'age', 'gender', 'marketing_target' ]
|
23
|
+
|
24
|
+
@@data_items = [['New York', 25, 'M', 'Y'],
|
25
|
+
['New York', 23, 'M', 'Y'],
|
26
|
+
['New York', 18, 'M', 'Y'],
|
27
|
+
['Chicago', 43, 'M', 'Y'],
|
28
|
+
['New York', 34, 'F', 'N'],
|
29
|
+
['Chicago', 33, 'F', 'Y'],
|
30
|
+
['New York', 31, 'F', 'N'],
|
31
|
+
['Chicago', 55, 'M', 'N'],
|
32
|
+
['New York', 58, 'F', 'N'],
|
33
|
+
['New York', 59, 'M', 'N'],
|
34
|
+
['Chicago', 71, 'M', 'N'],
|
35
|
+
['New York', 60, 'F', 'N'],
|
36
|
+
['Chicago', 85, 'F', 'Y']
|
37
|
+
]
|
38
|
+
|
39
|
+
|
40
|
+
def setup
|
41
|
+
Hyperpipes.send(:public, *Hyperpipes.protected_instance_methods)
|
42
|
+
@data_set = DataSet.new(:data_items => @@data_items, :data_labels => @@data_labels)
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_build_pipe
|
46
|
+
classifier = Hyperpipes.new
|
47
|
+
assert_equal [{}, {:max=>-1.0/0, :min=>1.0/0}, {}], classifier.build_pipe(@data_set)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_get_rules
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_build
|
55
|
+
assert_raise(ArgumentError) { Hyperpipes.new.build(DataSet.new) }
|
56
|
+
classifier = Hyperpipes.new.build(@data_set)
|
57
|
+
assert classifier.pipes.include?("Y")
|
58
|
+
assert classifier.pipes.include?("N")
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_eval
|
62
|
+
classifier = Hyperpipes.new.build(@data_set)
|
63
|
+
assert classifier
|
64
|
+
assert_equal('N', classifier.eval(['Chicago', 55, 'M']))
|
65
|
+
assert_equal('N', classifier.eval(['New York', 35, 'F']))
|
66
|
+
assert_equal('Y', classifier.eval(['New York', 25, 'M']))
|
67
|
+
assert_equal('Y', classifier.eval(['Chicago', 85, 'F']))
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_get_rules
|
71
|
+
classifier = Hyperpipes.new.build(@data_set)
|
72
|
+
age = 28
|
73
|
+
gender = "M"
|
74
|
+
marketing_target = nil
|
75
|
+
eval classifier.get_rules
|
76
|
+
assert_equal 'Y', marketing_target
|
77
|
+
age = 44
|
78
|
+
city='New York'
|
79
|
+
eval classifier.get_rules
|
80
|
+
assert_equal 'N', marketing_target
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
|