module RubyBrain class Network extend Forwardable def_delegators :@weights_set, :overwrite_weights, :get_weights_as_array attr_accessor :learning_rate def initialize(num_units_list) @layers = [] @num_units_list = num_units_list @weights_set = WeightContainer.new(@num_units_list) end def load_weights_from(weights_set_source) @weights_set.load_from(weights_set_source) init_network end # def overwrite_weights(weights_set_source) # @weights_set.overwrite_weights(weights_set_source) # end def init_network @layers = [] layer = Layer.new (@num_units_list[0] + 1).times do layer.append Nodes::ConstNode.new layer.output_weights = @weights_set.weights_of_order(0) end @layers << layer @num_units_list[1..-2].each_with_index do |num_units, i| layer = Layer.new layer.input_weights = @weights_set.weights_of_order(i) layer.output_weights = @weights_set.weights_of_order(i+1) (num_units).times do layer.append Nodes::Neuron.new end layer.append Nodes::ConstNode.new @layers << layer end layer = Layer.new layer.input_weights = @weights_set.weights_of_order(@num_units_list.size - 2) @num_units_list[-1].times do layer.append Nodes::Neuron.new end @layers << layer end # def get_weights_as_array # @weights_set.get_weights_as_array # end def get_forward_outputs(inputs) inputs.each_with_index do |input, i| @layers.first.nodes[i].value = input end a_layer_outputs = nil a_layer_inputs = @layers.first.forward_outputs @layers.each do |layer| a_layer_outputs = layer.forward_outputs(a_layer_inputs) a_layer_inputs = a_layer_outputs end a_layer_outputs end def run_backpropagate(backward_inputs) a_layer_outputs = nil a_layer_inputs = backward_inputs @layers.reverse[0..-2].each do |layer| a_layer_outputs = layer.backward_outputs(a_layer_inputs) a_layer_inputs = a_layer_outputs end a_layer_outputs end def update_weights @weights_set.each_weights_with_index do |weights, i| weights.each_with_index do |wl, j| wl.each_with_index do |w, k| wl[k] = w - (@learning_rate * @layers[i].nodes[j].this_output * @layers[i+1].nodes[k].this_backward_output) end end end end def update_weights_of_layer(layer_index) layer_index = @weights_set.num_sets + layer_index if layer_index < 0 @weights_set.each_weights_with_index do |weights, i| next if i != layer_index weights.each_with_index do |wl, j| wl.each_with_index do |w, k| wl[k] = w - (@learning_rate * @layers[i].nodes[j].this_output * @layers[i+1].nodes[k].this_backward_output) end end end end # def calculate_rms_error(training_inputs_set, training_outputs_set) # accumulated_errors = 0.0 # training_inputs_set.zip(training_outputs_set).each do |t_input, t_output| # forward_outputs = get_forward_outputs(t_input) # total_error_of_output_nodes = 0.0 # forward_outputs.zip(t_output).each do |o, t| # total_error_of_output_nodes += (o - t)**2 / 2.0 # end # accumulated_errors += total_error_of_output_nodes / forward_outputs.size # end # Math.sqrt(2.0 * accumulated_errors / training_inputs_set.size) # end def learn(inputs_set, outputs_set, max_training_count=50, tolerance=0.0, monitoring_channels=[]) raise RubyBrain::Exception::TrainingDataError if inputs_set.size != outputs_set.size # raise "inputs_set and outputs_set has different size!!!!" if inputs_set.size != outputs_set.size best_error = 9999999999999 best_weights_array = [] max_training_count.times do |i_training| accumulated_errors = 0.0 # for rms inputs_set.zip(outputs_set).each do |t_input, t_output| forward_outputs = get_forward_outputs(t_input) # for rms start total_error_of_output_nodes = forward_outputs.zip(t_output).reduce(0.0) do |a, output_pair| a + ((output_pair[0] - output_pair[1])**2 / 2.0) end # end accumulated_errors += total_error_of_output_nodes / forward_outputs.size # accumulated_errors += forward_outputs.zip(t_output).reduce(0.0) { |a, output_pair| a + ((output_pair[0] - output_pair[1])**2 / 2.0) } / forward_outputs.size # for rms end backward_inputs = forward_outputs.zip(t_output).map { |o, t| o - t } run_backpropagate(backward_inputs) update_weights end rms_error = Math.sqrt(2.0 * accumulated_errors / inputs_set.size) # for rms # rms_error = calculate_rms_error(inputs_set, outputs_set) puts "--> #{rms_error} (#{i_training}/#{max_training_count})" if rms_error < best_error puts "update best!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" best_error = rms_error best_weights_array = @weights_set.get_weights_as_array end puts "best: #{best_error}" break if rms_error <= tolerance end if monitoring_channels.include? :best_params_training File.open "best_weights_#{Time.now.to_i}.yml", 'w+' do |f| YAML.dump(best_weights_array, f) end end end def learn2(inputs_set, outputs_set, max_training_count=50, tolerance=0.0, monitoring_channels=[]) # looks like works well for networks which has many layers... [1, 10, 10, 10, 1], [1, 100, 100, 100, 1] # looks like NOT works well for networks which has many units in a layer... [1, 100, 1] raise RubyBrain::Exception::TrainingDataError if inputs_set.size != outputs_set.size # raise "inputs_set and outputs_set has different size!!!!" if inputs_set.size != outputs_set.size initial_learning_rate = @learning_rate rms_error = Float::INFINITY max_training_count.times do |i_training| accumulated_errors = 0.0 # for rms inputs_set.zip(outputs_set).each do |t_input, t_output| forward_outputs = get_forward_outputs(t_input) # for rms start total_error_of_output_nodes = forward_outputs.zip(t_output).reduce(0.0) do |a, output_pair| a + ((output_pair[0] - output_pair[1])**2 / 2.0) end # end error_of_this_training_data = total_error_of_output_nodes / forward_outputs.size accumulated_errors += error_of_this_training_data # accumulated_errors += forward_outputs.zip(t_output).reduce(0.0) { |a, output_pair| a + ((output_pair[0] - output_pair[1])**2 / 2.0) } / forward_outputs.size # for rms end # if error_of_this_training_data > rms_error**2/2.0 # @learning_rate *= 10.0 # end backward_inputs = forward_outputs.zip(t_output).map { |o, t| o - t } run_backpropagate(backward_inputs) update_weights # @learning_rate = initial_learning_rate end rms_error = Math.sqrt(2.0 * accumulated_errors / inputs_set.size) # for rms # rms_error = calculate_rms_error(inputs_set, outputs_set) puts "--> #{rms_error} (#{i_training}/#{max_training_count})" break if rms_error <= tolerance end end def learn_only_specified_layer(layer_index, inputs_set, outputs_set, max_training_count=50, tolerance=0.0) # looks like works well for networks which has many layers... [1, 10, 10, 10, 1], [1, 100, 100, 100, 1] # looks like NOT works well for networks which has many units in a layer... [1, 100, 1] raise "inputs_set and outputs_set has different size!!!!" if inputs_set.size != outputs_set.size initial_learning_rate = @learning_rate rms_error = Float::INFINITY max_training_count.times do |i_training| accumulated_errors = 0.0 # for rms inputs_set.zip(outputs_set).each do |t_input, t_output| forward_outputs = get_forward_outputs(t_input) # for rms start total_error_of_output_nodes = forward_outputs.zip(t_output).reduce(0.0) do |a, output_pair| a + ((output_pair[0] - output_pair[1])**2 / 2.0) end # end error_of_this_training_data = total_error_of_output_nodes / forward_outputs.size accumulated_errors += error_of_this_training_data # accumulated_errors += forward_outputs.zip(t_output).reduce(0.0) { |a, output_pair| a + ((output_pair[0] - output_pair[1])**2 / 2.0) } / forward_outputs.size # for rms end if error_of_this_training_data > rms_error**2/2.0 @learning_rate *= 10.0 end backward_inputs = forward_outputs.zip(t_output).map { |o, t| o - t } run_backpropagate(backward_inputs) update_weights_of_layer(layer_index) @learning_rate = initial_learning_rate end rms_error = Math.sqrt(2.0 * accumulated_errors / inputs_set.size) # for rms # rms_error = calculate_rms_error(inputs_set, outputs_set) puts "--> #{rms_error} (#{i_training}/#{max_training_count})" break if rms_error <= tolerance end end def dump_weights @weights_set.each_weights do |weights| pp weights end end def dump_weights_to_yaml(file_name=nil) @weights_set.dump_to_yaml(file_name) end def load_weights_from_yaml_file(yaml_file) @weights_set.load_from_yaml_file(yaml_file) end end end