Class: CooCoo::Network

Inherits:
Object show all
Defined in:
lib/coo-coo/network.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize {|_self| ... } ⇒ Network

Returns a new instance of Network.

Yields:

  • (_self)

Yield Parameters:



15
16
17
18
19
20
# File 'lib/coo-coo/network.rb', line 15

def initialize
  @layers = Array.new
  @age = 0
  @command = [ $0 ] + ARGV
  yield(self) if block_given?
end

Instance Attribute Details

#activation_functionObject (readonly)

Returns the value of attribute activation_function.



12
13
14
# File 'lib/coo-coo/network.rb', line 12

def activation_function
  @activation_function
end

#ageObject (readonly)

Returns the value of attribute age.



12
13
14
# File 'lib/coo-coo/network.rb', line 12

def age
  @age
end

#commandObject

Returns the value of attribute command.



13
14
15
# File 'lib/coo-coo/network.rb', line 13

def command
  @command
end

#commentsObject

Returns the value of attribute comments.



13
14
15
# File 'lib/coo-coo/network.rb', line 13

def comments
  @comments
end

Class Method Details

.from_a(layers) ⇒ Object



212
213
214
# File 'lib/coo-coo/network.rb', line 212

def from_a(layers)
  self.new().update_from_a!(layers)
end

.from_hash(h) ⇒ Object



216
217
218
# File 'lib/coo-coo/network.rb', line 216

def from_hash(h)
  self.new.update_from_hash!(h)
end

.load(path) ⇒ Object



220
221
222
# File 'lib/coo-coo/network.rb', line 220

def load(path)
  self.new().load!(path)
end

Instance Method Details

#adjust_weights!(deltas) ⇒ Object



140
141
142
143
144
145
146
147
# File 'lib/coo-coo/network.rb', line 140

def adjust_weights!(deltas)
  @layers.each_with_index do |layer, i|
    layer.adjust_weights!(deltas[i])
  end

  @age += 1
  self
end

#backprop(inputs, outputs, errors, hidden_state = nil) ⇒ Object



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/coo-coo/network.rb', line 109

def backprop(inputs, outputs, errors, hidden_state = nil)
  hidden_state ||= Hash.new
  d = @layers.reverse_each.each_with_index.inject([]) do |acc, (layer, i)|
    input = if i < (@layers.size - 1)
              outputs[@layers.size - i - 2]
            else
              prep_input(inputs) # TODO condition prep_input
            end
    #CooCoo.debug("#{self.class.name}.#{__method__}\t#{i} #{@layers.size - i - 1}\t#{input.size}\t#{outputs.size}")
    deltas, hidden_state = layer.backprop(input,
                                          outputs[@layers.size - i - 1],
                                          errors,
                                          hidden_state)
    errors = layer.transfer_error(deltas)
    acc.unshift(deltas)
  end

  return Sequence[d], hidden_state
end

#final_output(outputs) ⇒ Object



73
74
75
# File 'lib/coo-coo/network.rb', line 73

def final_output(outputs)
  outputs.last
end

#forward(input, hidden_state = nil, flattened = false, processed = false) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/coo-coo/network.rb', line 77

def forward(input, hidden_state = nil, flattened = false, processed = false)
  unless flattened || input.kind_of?(CooCoo::Vector)
    input = CooCoo::Vector[input.to_a.flatten, num_inputs]
  end

  hidden_state ||= Hash.new

  output = if processed
             input
           else
             prep_input(input)
           end
  
  outputs = @layers.each_with_index.inject([]) do |acc, (layer, i)|
    #debug("Layer: #{i} #{layer.num_inputs} #{layer.size}")
    #debug("Input: #{input}")
    #debug("Weights: #{layer.neurons[0].weights}")
    output, hidden_state = layer.forward(output, hidden_state)
    acc << output
    #debug("Output: #{input}")
  end

  return outputs, hidden_state
end

#layer(new_layer) ⇒ Object



42
43
44
45
# File 'lib/coo-coo/network.rb', line 42

def layer(new_layer)
  @layers << new_layer
  self
end

#layer_index(layer) ⇒ Object



38
39
40
# File 'lib/coo-coo/network.rb', line 38

def layer_index(layer)
  @layers.find_index { |l| l.eql?(layer) }
end

#layersObject



34
35
36
# File 'lib/coo-coo/network.rb', line 34

def layers
  @layers
end

#learn(input, expecting, rate, cost_function = CostFunctions::MeanSquare, hidden_state = nil) ⇒ Object



162
163
164
165
166
167
168
169
170
171
172
# File 'lib/coo-coo/network.rb', line 162

def learn(input, expecting, rate, cost_function = CostFunctions::MeanSquare, hidden_state = nil)
  hidden_state ||= Hash.new
  output, hidden_state = forward(input, hidden_state)
  cost = cost_function.derivative(prep_input(expecting), output.last)
  deltas, hidden_state = backprop(input, output, cost, hidden_state)
  update_weights!(input, output, deltas * rate)
  return self, hidden_state
rescue
  CooCoo.debug("Network#learn caught #{$!}", input, expecting)
  raise
end

#load!(path) ⇒ Object

Raises:

  • (RuntimeError)


180
181
182
183
184
185
186
187
# File 'lib/coo-coo/network.rb', line 180

def load!(path)
  yaml = YAML.load(File.read(path))
  raise RuntimeError.new("Invalid YAML definition in #{path}") if yaml.nil?
    
  update_from_hash!(yaml)

  self
end

#num_inputsObject



22
23
24
# File 'lib/coo-coo/network.rb', line 22

def num_inputs
  @layers.first.num_inputs
end

#num_layersObject



30
31
32
# File 'lib/coo-coo/network.rb', line 30

def num_layers
  @layers.size
end

#num_outputsObject



26
27
28
# File 'lib/coo-coo/network.rb', line 26

def num_outputs
  @layers.last.size
end

#output_activation_functionObject



56
57
58
59
60
61
62
63
# File 'lib/coo-coo/network.rb', line 56

def output_activation_function
  unless @output_activation_function
    layer = @layers.reverse.find { |l| l.activation_function }
    @output_activation_function = layer.activation_function
  end

  @output_activation_function
end

#predict(input, hidden_state = nil, flattened = false, processed = false) ⇒ Object



102
103
104
105
106
107
# File 'lib/coo-coo/network.rb', line 102

def predict(input, hidden_state = nil, flattened = false, processed = false)
  hidden_state ||= Hash.new
  outputs, hidden_state = forward(input, hidden_state, flattened, processed)
  out = final_output(outputs)
  return out, hidden_state
end

#prep_input(input) ⇒ Object



65
66
67
# File 'lib/coo-coo/network.rb', line 65

def prep_input(input)
  activation_function.prep_input(input)
end

#prep_output_target(target) ⇒ Object



69
70
71
# File 'lib/coo-coo/network.rb', line 69

def prep_output_target(target)
  output_activation_function.prep_output_target(target)
end

#save(path) ⇒ Object



174
175
176
177
178
# File 'lib/coo-coo/network.rb', line 174

def save(path)
  File.write_to(path) do |f|
    f.write(to_hash.to_yaml)
  end
end

#to_hashObject



203
204
205
206
207
208
209
# File 'lib/coo-coo/network.rb', line 203

def to_hash
  { age: @age,
    command: @command,
    comments: @comments,
    layers: @layers.collect { |l| l.to_hash(self) }
  }
end

#transfer_errors(deltas) ⇒ Object



129
130
131
132
133
# File 'lib/coo-coo/network.rb', line 129

def transfer_errors(deltas)
  @layers.zip(deltas).collect do |layer, delta|
    layer.transfer_error(delta)
  end
end

#update_from_hash!(h) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/coo-coo/network.rb', line 189

def update_from_hash!(h)
  @layers = Array.new
  
  h[:layers].each do |layer_hash|
    @layers << CooCoo::LayerFactory.from_hash(layer_hash, self)
  end

  @age = h.fetch(:age, 0)
  @command = h.fetch(:command, nil)
  @comments = h.fetch(:comments) { Array.new }

  self
end

#update_weights!(input, outputs, deltas) ⇒ Object



135
136
137
138
# File 'lib/coo-coo/network.rb', line 135

def update_weights!(input, outputs, deltas)
  adjust_weights!(weight_deltas(input, outputs, deltas))
  self
end

#weight_deltas(input, outputs, deltas) ⇒ Object



149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/coo-coo/network.rb', line 149

def weight_deltas(input, outputs, deltas)
  d = @layers.each_with_index.collect do |layer, i|
    inputs = if i != 0
               outputs[i - 1]
             else
               prep_input(input)
             end
    layer.weight_deltas(inputs, deltas[i])
  end

  d
end