class Brain::NeuralNetwork
Public Class Methods
new(options = {})
click to toggle source
# File lib/brain/neuralnetwork.rb, line 8 def initialize(options = {}) @learning_rate = options[:learning_rate] || 0.3 @momentum = options[:momentum] || 0.1 @hidden_sizes = options[:hidden_layers] @binary_thresh = options[:binary_thresh] || 0.5 end
Public Instance Methods
adjust_weights(learning_rate)
click to toggle source
# File lib/brain/neuralnetwork.rb, line 148 def adjust_weights(learning_rate) (1..@output_layer).each do |layer| incoming = @outputs[layer - 1] (0...@sizes[layer]).each do |node| delta = @deltas[layer][node] (0...incoming.length).each do |k| change = @changes[layer][node][k] change = (learning_rate * delta * incoming[k]) + (@momentum * change) @changes[layer][node][k] = change @weights[layer][node][k] += change end @biases[layer][node] += learning_rate * delta end end end
calculate_deltas(target)
click to toggle source
# File lib/brain/neuralnetwork.rb, line 128 def calculate_deltas(target) (0..@output_layer).to_a.reverse.each do |layer| (0...@sizes[layer]).each do |node| output = @outputs[layer][node] error = 0 if layer == @output_layer error = target[node] - output else deltas = @deltas[layer + 1] (0...deltas.length).each do |k| error += deltas[k] * @weights[layer + 1][k][node] end end @errors[layer][node] = error @deltas[layer][node] = error * output * (1 - output) end end end
format_data(data)
click to toggle source
# File lib/brain/neuralnetwork.rb, line 168 def format_data(data) unless data.is_a? Array data = [data] end #turn sparse hash input into arrays with 0s as filler unless data[0][:input].is_a? Array @input_lookup = Lookup.build_lookup data.map {|d| d[:input]} unless @input_lookup data.map! do |datum| array = Lookup.to_array @input_lookup, datum[:input] datum.merge({ input: array }) end end unless data[0][:output].is_a? Array @output_lookup = Lookup.build_lookup data.map {|d| d[:output]} unless @output_lookup data.map! do |datum| array = Lookup.to_array @output_lookup, datum[:output] datum.merge({ output: array }) end end data end
from_json(json)
click to toggle source
# File lib/brain/neuralnetwork.rb, line 241 def from_json(json) json = JSON.parse(json).deep_symbolize_keys size = json[:layers].length @output_layer = size - 1 @sizes = Array.new size @weights = Array.new size @biases = Array.new size @outputs = Array.new size (0..@output_layer).each do |i| layer = json[:layers][i] if i == 0 and (!layer[0] or json[:input_lookup]) @input_lookup = Lookup.lookup_from_hash layer elsif i == @output_layer and (!layer[0] or json[:output_lookup]) @output_lookup = Lookup.lookup_from_hash layer end nodes = layer.keys @sizes[i] = nodes.length @weights[i] = [] @biases[i] = [] @outputs[i] = [] (0...nodes.length).each do |j| node = nodes[j] @biases[i][j] = layer[node][:bias] @weights[i][j] = layer[node][:weights] @weights[i][j] = @weights[i][j].values unless @weights[i][j].nil? end end end
init(sizes)
click to toggle source
# File lib/brain/neuralnetwork.rb, line 15 def init(sizes) @sizes = sizes @output_layer = @sizes.length - 1 @biases = [] # weights for bias nodes @weights = [] @outputs = [] # state for training @deltas = [] @changes = [] # for momentum @errors = [] (0..@output_layer).each do |layer| size = @sizes[layer] @deltas[layer] = Array.new size, 0 @errors[layer] = Array.new size, 0 @outputs[layer] = Array.new size, 0 if layer > 0 @biases[layer] = randos size @weights[layer] = Array.new size @changes[layer] = Array.new size (0...size).each do |node| prev_size = @sizes[layer - 1] @weights[layer][node] = randos prev_size @changes[layer][node] = Array.new prev_size, 0 end end end end
run(input)
click to toggle source
# File lib/brain/neuralnetwork.rb, line 48 def run(input) input = Lookup.to_array(@input_lookup, input) if @input_lookup output = run_input input output = Lookup.to_hash(@output_lookup, output) if @output_lookup output end
run_input(input)
click to toggle source
# File lib/brain/neuralnetwork.rb, line 57 def run_input(input) @outputs[0] = input output = 0 (1..@output_layer).each do |layer| (0...@sizes[layer]).each do |node| weights = @weights[layer][node] sum = @biases[layer][node] (0...weights.length).each do |k| sum += weights[k] * input[k] end @outputs[layer][node] = 1 / (1 + Math.exp(-sum)) end output = input = @outputs[layer] end output end
to_json()
click to toggle source
# File lib/brain/neuralnetwork.rb, line 193 def to_json # make json look like: # { # layers: [ # { x: {}, # y: {}}, # {'0': {bias: -0.98771313, weights: {x: 0.8374838, y: 1.245858}, # '1': {bias: 3.48192004, weights: {x: 1.7825821, y: -2.67899}}}, # { f: {bias: 0.27205739, weights: {'0': 1.3161821, '1': 2.00436}}} # ] # } layers = [] (0..@output_layer).each do |layer| layers[layer] = {} if layer == 0 and @input_lookup nodes = @input_lookup.keys elsif layer == @output_layer and @output_lookup nodes = @output_lookup.keys else nodes = (0...@sizes[layer]).to_a end (0...nodes.length).each do |j| node = nodes[j] layers[layer][node] = {} if layer > 0 layers[layer][node][:bias] = @biases[layer][j] layers[layer][node][:weights] = {} layers[layer - 1].each do |k,v| index = k if layer == 1 and @input_lookup index = @input_lookup[k] end layers[layer][node][:weights][k] = @weights[layer][j][index] end end end end { layers: layers, output_lookup: !!@output_lookup, input_lookup: !!@input_lookup }.to_json end
train(data, options = {})
click to toggle source
# File lib/brain/neuralnetwork.rb, line 77 def train(data, options = {}) data = format_data data iterations = options[:iterations] || 20000 error_thresh = options[:error_thresh] || 0.003 log = options[:log] || false log_period = options[:log_period] || 10 learning_rate = options[:learning_rate] || @learning_rate || 0.3 input_size = data[0][:input].length output_size = data[0][:output].length hidden_sizes = @hidden_sizes hidden_sizes = [[3, (input_size / 2.0).floor].max] unless hidden_sizes sizes = [input_size, hidden_sizes, output_size].flatten init sizes error = 1 done_iterations = iterations (0...iterations).each do |i| unless error > error_thresh done_iterations = i break end sum = 0 data.each do |d| err = train_pattern d[:input], d[:output], learning_rate sum += err end error = sum / data.length puts "iterations: #{i}, training error: #{error}" if log and (i % log_period == 0) end { error: error, iterations: done_iterations } end
train_pattern(input, target, learning_rate)
click to toggle source
# File lib/brain/neuralnetwork.rb, line 117 def train_pattern(input, target, learning_rate) learning_rate ||= @learning_rate # forward propogate run_input input calculate_deltas target adjust_weights learning_rate mse @errors[@output_layer] end
Private Instance Methods
mse(errors)
click to toggle source
# File lib/brain/neuralnetwork.rb, line 286 def mse(errors) # mean squared error sum = 0 errors.each do |e| sum += e ** 2 end sum / errors.length end
random_weight()
click to toggle source
# File lib/brain/neuralnetwork.rb, line 277 def random_weight Random.rand * 0.4 - 0.2 end
randos(size)
click to toggle source
# File lib/brain/neuralnetwork.rb, line 281 def randos(size) array = Array.new size array.map! {|item| item = random_weight} end