brain 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +10 -0
- data/README.txt +53 -0
- data/lib/brain.rb +18 -0
- data/lib/brain/hopfield.rb +75 -0
- data/lib/brain/hopfield/sample.rb +107 -0
- data/lib/brain/version.rb +9 -0
- data/spec/brain/hopfield/sample_spec.rb +87 -0
- data/spec/brain/hopfield_spec.rb +43 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +11 -0
- metadata +77 -0
data/History.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
= brain
|
2
|
+
|
3
|
+
* http://brain.rubyforge.org
|
4
|
+
* http://github.com/brainopia/brain
|
5
|
+
|
6
|
+
== DESCRIPTION:
|
7
|
+
|
8
|
+
Implements several types of Neural Networks such as multilayer perceptron (data classification), Kohonen net (data clusterization), Hopfield net (data association).
|
9
|
+
|
10
|
+
== FEATURES:
|
11
|
+
|
12
|
+
Currently supports:
|
13
|
+
* Hopfield net
|
14
|
+
|
15
|
+
== SYNOPSIS:
|
16
|
+
|
17
|
+
Basic examples:
|
18
|
+
|
19
|
+
* Hopfield net
|
20
|
+
|
21
|
+
sample = Brain::Hopfield[[-1, -1, -1, -1], [1, 1, 1, 1]].associate [1, 1, -1, 1]
|
22
|
+
sample.run until sample.associated?
|
23
|
+
sample.current # => [1, 1, 1, 1]
|
24
|
+
|
25
|
+
|
26
|
+
== INSTALL:
|
27
|
+
|
28
|
+
sudo gem install brain
|
29
|
+
|
30
|
+
== LICENSE:
|
31
|
+
|
32
|
+
(The MIT License)
|
33
|
+
|
34
|
+
Copyright (c) 2008 Ravil Bayramgalin
|
35
|
+
|
36
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
37
|
+
a copy of this software and associated documentation files (the
|
38
|
+
'Software'), to deal in the Software without restriction, including
|
39
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
40
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
41
|
+
permit persons to whom the Software is furnished to do so, subject to
|
42
|
+
the following conditions:
|
43
|
+
|
44
|
+
The above copyright notice and this permission notice shall be
|
45
|
+
included in all copies or substantial portions of the Software.
|
46
|
+
|
47
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
48
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
49
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
50
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
51
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
52
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
53
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/brain.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Defines Brain module.
|
3
|
+
=end
|
4
|
+
|
5
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
6
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
7
|
+
|
8
|
+
require 'mathn'
|
9
|
+
|
10
|
+
=begin rdoc
|
11
|
+
Contains neural network classes: Brain::Hopfield, Brain::Kohonen and Brain::Perceptron.
|
12
|
+
|
13
|
+
Classes are autoloaded with the first invoking.
|
14
|
+
=end
|
15
|
+
|
16
|
+
module Brain
|
17
|
+
autoload 'Hopfield', 'brain/hopfield'
|
18
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Defines Brain::Hopfield class.
|
3
|
+
=end
|
4
|
+
|
5
|
+
=begin rdoc
|
6
|
+
Represents a Hopfield net.
|
7
|
+
|
8
|
+
net = Brain::Hopfield[[-1, -1, -1, -1], [1, 1, 1, 1]]
|
9
|
+
sample = net.associate [1, 1, -1, 1]
|
10
|
+
sample.run until sample.associated?
|
11
|
+
sample.current # => [1, 1, 1, 1]
|
12
|
+
sample.iterations # => 2
|
13
|
+
|
14
|
+
In this example, it can take up to 4 iterations to associate the sample (actual value depends on a random order of updating neurons).
|
15
|
+
=end
|
16
|
+
class Brain::Hopfield
|
17
|
+
require 'brain/hopfield/sample'
|
18
|
+
|
19
|
+
# Shortcut for creation of a new Hopfield object.
|
20
|
+
def self.[](*learning_samples)
|
21
|
+
new learning_samples
|
22
|
+
end
|
23
|
+
|
24
|
+
# Learning samples must be not empty and have same dimension.
|
25
|
+
def initialize(learning_samples)
|
26
|
+
@learning_samples = learning_samples
|
27
|
+
learning_samples_must_be_not_empty!
|
28
|
+
|
29
|
+
@dimension = @learning_samples.first.size
|
30
|
+
learning_samples_must_have_same_dimension!
|
31
|
+
|
32
|
+
calculate_weight_matrix
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns a Brain::Hopfield::Sample object which can be used for an association of given sample.
|
36
|
+
def associate(sample)
|
37
|
+
Sample.new self, sample
|
38
|
+
end
|
39
|
+
|
40
|
+
# Learning samples attribute.
|
41
|
+
def learning_samples
|
42
|
+
@learning_samples
|
43
|
+
end
|
44
|
+
|
45
|
+
# Weights attribute.
|
46
|
+
def weights
|
47
|
+
@weights
|
48
|
+
end
|
49
|
+
|
50
|
+
# Dimension attribute.
|
51
|
+
def dimension
|
52
|
+
@dimension
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
def learning_samples_must_be_not_empty!
|
58
|
+
raise ArgumentError if learning_samples.empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
def learning_samples_must_have_same_dimension!
|
62
|
+
raise ArgumentError unless learning_samples.all? {|sample| sample.size == dimension }
|
63
|
+
end
|
64
|
+
|
65
|
+
def calculate_weight_matrix
|
66
|
+
@weights = Matrix.zero(dimension)
|
67
|
+
|
68
|
+
learning_samples.each do |sample|
|
69
|
+
vector = Matrix.row_vector sample
|
70
|
+
@weights += vector.transpose * vector
|
71
|
+
end
|
72
|
+
|
73
|
+
@weights = Matrix[*@weights.to_a.each_with_index {|row, i| row[i] = 0 }]
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Defines Brain::Hopfield::Sample class.
|
3
|
+
=end
|
4
|
+
|
5
|
+
=begin rdoc
|
6
|
+
Uses a given Hopfield object for a successive accosiation with a given sample.
|
7
|
+
|
8
|
+
When there is too much noise in the initial sample, it can associate it with an inverse version of a learning sample. Example:
|
9
|
+
|
10
|
+
net = Brain::Hopfield[[1, 1, 1, 1]]
|
11
|
+
sample = net.associate [1, -1, -1, -1]
|
12
|
+
sample.run until sample.associated?
|
13
|
+
sample.current # => [-1, -1, -1, -1]
|
14
|
+
|
15
|
+
Initial sample [1, -1, -1, -1] containes too much noise to be comparable with [1, 1, 1, 1]. So sample is associated with the inverse version of [1, 1, 1, 1] which equals [-1, -1, -1, -1].
|
16
|
+
=end
|
17
|
+
class Brain::Hopfield::Sample
|
18
|
+
|
19
|
+
# A sample must have same dimension as a Hopfield object.
|
20
|
+
def initialize(net, sample)
|
21
|
+
@net, @initial, @indexes = net, sample, []
|
22
|
+
@current, @iterations, @associated = initial, 0, false
|
23
|
+
|
24
|
+
sample_must_have_same_dimension_as_net!
|
25
|
+
@associated = true if known_sample?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns true if the current sample is accosiated.
|
29
|
+
def associated?
|
30
|
+
@associated
|
31
|
+
end
|
32
|
+
|
33
|
+
# Used to update a state of the current sample in a random order. A number of updated positions is controlled with a given parameter.
|
34
|
+
def run(number = 1)
|
35
|
+
number.times do
|
36
|
+
break if associated?
|
37
|
+
update_next_neuron
|
38
|
+
@associated = true if minimal_energy? or known_sample?
|
39
|
+
end
|
40
|
+
current
|
41
|
+
end
|
42
|
+
|
43
|
+
# Measure of closerness to learning samples. The least energy state corresponds to an association with a learning sample.
|
44
|
+
def energy
|
45
|
+
(Matrix.row_vector(current) * net.weights).row(0).inner_product current
|
46
|
+
end
|
47
|
+
|
48
|
+
# A corresponding Hopfield object.
|
49
|
+
def net
|
50
|
+
@net
|
51
|
+
end
|
52
|
+
|
53
|
+
# Initial state of sample.
|
54
|
+
def initial
|
55
|
+
@initial
|
56
|
+
end
|
57
|
+
|
58
|
+
# Current state of sample.
|
59
|
+
def current
|
60
|
+
@current
|
61
|
+
end
|
62
|
+
|
63
|
+
# Number of itererations.
|
64
|
+
def iterations
|
65
|
+
@iterations
|
66
|
+
end
|
67
|
+
|
68
|
+
protected
|
69
|
+
|
70
|
+
def sample_must_have_same_dimension_as_net!
|
71
|
+
raise ArgumentError unless current.size == net.dimension
|
72
|
+
end
|
73
|
+
|
74
|
+
def known_sample?
|
75
|
+
net.learning_samples.any? {|sample| sample == current }
|
76
|
+
end
|
77
|
+
|
78
|
+
def minimal_energy?
|
79
|
+
@energy_changed = true unless @previous_energy == energy
|
80
|
+
if @indexes.empty?
|
81
|
+
@energy_changed ? (@energy_changed = false) : true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def activation_argument(neuron_index)
|
86
|
+
net.weights.row(neuron_index).inner_product current
|
87
|
+
end
|
88
|
+
|
89
|
+
def activation_function(neuron_index)
|
90
|
+
activation = activation_argument neuron_index
|
91
|
+
return 1 if activation > 0
|
92
|
+
return -1 if activation < 0
|
93
|
+
current[neuron_index]
|
94
|
+
end
|
95
|
+
|
96
|
+
def next_neuron
|
97
|
+
@indexes = Array.new(net.dimension) {|i| i } if @indexes.empty?
|
98
|
+
@indexes.delete_at rand(@indexes.size)
|
99
|
+
end
|
100
|
+
|
101
|
+
def update_next_neuron
|
102
|
+
@previous_energy = energy
|
103
|
+
neuron_index = next_neuron
|
104
|
+
current[neuron_index] = activation_function neuron_index
|
105
|
+
@iterations += 1
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
|
3
|
+
describe Brain::Hopfield::Sample do
|
4
|
+
it "should be initialized with a Hopfield net and a sample" do
|
5
|
+
lambda { Hopfield::Sample.new Hopfield[ [1, 1, 1] ], [-1, 1, -1] }.
|
6
|
+
should_not raise_error(ArgumentError)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should raise error if the sample has a different arity then the net" do
|
10
|
+
lambda { Hopfield::Sample.new Hopfield[ [1, 1, 1] ], [-1, 1, -1, 1] }.
|
11
|
+
should raise_error(ArgumentError)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should check if the sample belongs to learning samples" do
|
15
|
+
sample = Hopfield::Sample.new(Hopfield[ [1, 1, 1] ], [1, 1, 1])
|
16
|
+
sample.associated?.should == true
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '(after initialization)' do
|
20
|
+
before(:each) do
|
21
|
+
@learned_sample = [1, -1, 1, -1, 1, -1, 1, -1, 1, -1]
|
22
|
+
@tested_sample = [-1, -1, -1, -1, -1, -1, 1, 1, 1, -1]
|
23
|
+
|
24
|
+
@net = Hopfield[ @learned_sample ]
|
25
|
+
@sample = Hopfield::Sample.new @net, @tested_sample
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should start an iterations count with 0" do
|
29
|
+
@sample.iterations.should == 0
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return a status of training" do
|
33
|
+
@sample.associated?.should == false
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should return a current associated sample" do
|
37
|
+
@sample.current.should == @tested_sample
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should return an initial sample" do
|
41
|
+
@sample.initial.should == @tested_sample
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should be ran a given number of times" do
|
45
|
+
@sample.run(50).should == @learned_sample
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should increment iterations after an every run" do
|
49
|
+
@sample.stub!(:associated?).and_return(false)
|
50
|
+
3.times { @sample.run(3) }
|
51
|
+
@sample.iterations.should == 9
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should stop incrementing iterations after it's associated" do
|
55
|
+
@sample.stub!(:associated?).and_return(false)
|
56
|
+
@sample.run(4)
|
57
|
+
@sample.stub!(:associated?).and_return(true)
|
58
|
+
@sample.run(4)
|
59
|
+
@sample.iterations.should == 4
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should be ran by default one time" do
|
63
|
+
@sample.run
|
64
|
+
@sample.iterations.should == 1
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should return an using net" do
|
68
|
+
@sample.net.should == @net
|
69
|
+
end
|
70
|
+
|
71
|
+
it "shouldn't increase energy" do
|
72
|
+
previous_energy = @sample.energy
|
73
|
+
until @sample.associated?
|
74
|
+
@sample.run
|
75
|
+
previous_energy.should_not > @sample.energy
|
76
|
+
previous_energy = @sample.energy
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should return an inverse version of the learning sample if there is too much noise in the initial sample" do
|
81
|
+
net = Brain::Hopfield[[1, 1, 1, 1]]
|
82
|
+
sample = net.associate [1, -1, -1, -1]
|
83
|
+
sample.run until sample.associated?
|
84
|
+
sample.current.should == [-1, -1, -1, -1]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe Brain::Hopfield do
|
4
|
+
it "should be created with learning samples" do
|
5
|
+
lambda { Hopfield.new }.should raise_error(ArgumentError)
|
6
|
+
lambda { Hopfield.new [] }.should raise_error(ArgumentError)
|
7
|
+
lambda { Hopfield.new [[1, 1], [1, 1]] }.should_not raise_error
|
8
|
+
end
|
9
|
+
|
10
|
+
it "learning samples should have a same arity" do
|
11
|
+
lambda { Hopfield.new [[-1, -1], [1]] }.should raise_error(ArgumentError)
|
12
|
+
lambda { Hopfield.new [[-1, -1], [1, 1]] }.should_not raise_error
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should have a shortcut [] for creation" do
|
16
|
+
Hopfield[[1, 1], [1, 1]].should be_instance_of(Hopfield)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '(after initialization)' do
|
20
|
+
before(:each) do
|
21
|
+
@net = Hopfield[ [-1, 1, -1], [1, -1, 1] ]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should return a weight matrix" do
|
25
|
+
@net.weights.should == Matrix[ [ 0, -2, 2],
|
26
|
+
[-2, 0, -2],
|
27
|
+
[ 2, -2, 0] ]
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should return an array of learning samples" do
|
31
|
+
@net.learning_samples.should == [ [-1, 1, -1], [1, -1, 1] ]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should return an arity" do
|
35
|
+
@net.dimension.should == 3
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should create an object for an association with a given sample" do
|
39
|
+
associated_sample = @net.associate([1, 1, 1])
|
40
|
+
associated_sample.should be_instance_of(Hopfield::Sample)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: brain
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ravil Bayramgalin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-07-12 00:00:00 +04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.7.0
|
24
|
+
version:
|
25
|
+
description: Implementation of fundamental types of neural networks which includes multilayer perceptron, Kohonen net and Hopfield net.
|
26
|
+
email:
|
27
|
+
- ravwar@gmail.com
|
28
|
+
executables: []
|
29
|
+
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files:
|
33
|
+
- History.txt
|
34
|
+
- README.txt
|
35
|
+
files:
|
36
|
+
- History.txt
|
37
|
+
- README.txt
|
38
|
+
- lib/brain.rb
|
39
|
+
- lib/brain/hopfield.rb
|
40
|
+
- lib/brain/hopfield/sample.rb
|
41
|
+
- lib/brain/version.rb
|
42
|
+
- spec/brain/hopfield/sample_spec.rb
|
43
|
+
- spec/brain/hopfield_spec.rb
|
44
|
+
- spec/spec.opts
|
45
|
+
- spec/spec_helper.rb
|
46
|
+
has_rdoc: true
|
47
|
+
homepage: http://brain.rubyforge.org
|
48
|
+
post_install_message: |-
|
49
|
+
|
50
|
+
For more information on brain, see http://brain.rubyforge.org
|
51
|
+
rdoc_options:
|
52
|
+
- --main
|
53
|
+
- README.txt
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: "0"
|
61
|
+
version:
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
version:
|
68
|
+
requirements: []
|
69
|
+
|
70
|
+
rubyforge_project: brain
|
71
|
+
rubygems_version: 1.2.0
|
72
|
+
signing_key:
|
73
|
+
specification_version: 2
|
74
|
+
summary: Implementation of fundamental types of neural networks which includes multilayer perceptron, Kohonen net and Hopfield net.
|
75
|
+
test_files:
|
76
|
+
- spec/brain/hopfield/sample_spec.rb
|
77
|
+
- spec/brain/hopfield_spec.rb
|