2012-12-22 27 views
6

नहीं सीख सकता है, मैं तंत्रिका नेटवर्क के बारे में जानने की कोशिश कर रहा हूं और एक सरल, बैक-प्रोपेगेशन, तंत्रिका नेटवर्क को कोडित करता हूं जो सिग्मोइड सक्रियण कार्यों, यादृच्छिक वजन प्रारंभिकरण, और सीखने/ढाल गति का उपयोग करता है।सरल तंत्रिका नेटवर्क XOR

जब 2 इनपुट, 2 छुपे हुए नोड्स के साथ कॉन्फ़िगर किया गया, और 1 यह XOR और AND सीखने में विफल रहता है। हालांकि, यह सही ढंग से सीख जाएगा या।

मैं यह देखने में असफल रहा कि मैंने क्या किया है और इसलिए किसी भी मदद की सराहना की जाएगी।

धन्यवाद

संपादित करें: के रूप में कहा गया है, मैं 2 छिपा नोड्स के साथ परीक्षण किया, लेकिन नीचे कोड 3. के विन्यास मैं बस 3 छिपा नोड्स का उपयोग कर चल रहे परीक्षणों के बाद 2 को यह वापस बदलने के लिए भूल गया पता चलता है।

network.rb:

module Neural 

class Network 

    attr_accessor :num_inputs, :num_hidden_nodes, :num_output_nodes, :input_weights, :hidden_weights, :hidden_nodes, 
        :output_nodes, :inputs, :output_error_gradients, :hidden_error_gradients, 
        :previous_input_weight_deltas, :previous_hidden_weight_deltas 

    def initialize(config) 
     initialize_input(config) 
     initialize_nodes(config) 
     initialize_weights 
    end 

    def initialize_input(config) 
     self.num_inputs = config[:inputs] 
     self.inputs = Array.new(num_inputs+1) 
     self.inputs[-1] = -1 
    end 

    def initialize_nodes(config) 
     self.num_hidden_nodes = config[:hidden_nodes] 
     self.num_output_nodes = config[:output_nodes] 
     # treat threshold as an additional input/hidden node with no incoming inputs and a value of -1 
     self.output_nodes = Array.new(num_output_nodes) 
     self.hidden_nodes = Array.new(num_hidden_nodes+1) 
     self.hidden_nodes[-1] = -1 
    end 

    def initialize_weights 
     # treat threshold as an additional input/hidden node with no incoming inputs and a value of -1 
     self.input_weights = Array.new(hidden_nodes.size){Array.new(num_inputs+1)} 
     self.hidden_weights = Array.new(output_nodes.size){Array.new(num_hidden_nodes+1)} 
     set_random_weights(input_weights) 
     set_random_weights(hidden_weights) 
     self.previous_input_weight_deltas = Array.new(hidden_nodes.size){Array.new(num_inputs+1){0}} 
     self.previous_hidden_weight_deltas = Array.new(output_nodes.size){Array.new(num_hidden_nodes+1){0}} 
    end 

    def set_random_weights(weights) 
     (0...weights.size).each do |i| 
      (0...weights[i].size).each do |j| 
       weights[i][j] = (rand(100) - 49).to_f/100 
      end 
     end 
    end 

    def calculate_node_values(inputs) 
     inputs.each_index do |i| 
      self.inputs[i] = inputs[i] 
     end 

     set_node_values(self.inputs, input_weights, hidden_nodes) 
     set_node_values(hidden_nodes, hidden_weights, output_nodes) 
    end 

    def set_node_values(values, weights, nodes) 
     (0...weights.size).each do |i| 
      nodes[i] = Network::sigmoid(values.zip(weights[i]).map{|v,w| v*w}.inject(:+)) 
     end 
    end 

    def predict(inputs) 
     calculate_node_values(inputs) 
     output_nodes.size == 1 ? output_nodes[0] : output_nodes 
    end 

    def train(inputs, desired_results, learning_rate, momentum_rate) 
     calculate_node_values(inputs) 
     backpropogate_weights(desired_results, learning_rate, momentum_rate) 
    end 

    def backpropogate_weights(desired_results, learning_rate, momentum_rate) 
     output_error_gradients = calculate_output_error_gradients(desired_results) 
     hidden_error_gradients = calculate_hidden_error_gradients(output_error_gradients) 
     update_all_weights(inputs, desired_results, hidden_error_gradients, output_error_gradients, learning_rate, momentum_rate) 
    end 

    def self.sigmoid(x) 
     1.0/(1 + Math::E**-x) 
    end 

    def self.dsigmoid(x) 
     sigmoid(x) * (1 - sigmoid(x)) 
    end 

    def calculate_output_error_gradients(desired_results) 
     desired_results.zip(output_nodes).map{|desired, result| (desired - result) * Network::dsigmoid(result)} 
    end 

    def reversed_hidden_weights 
     # array[hidden node][weights to output nodes] 
     reversed = Array.new(hidden_nodes.size){Array.new(output_nodes.size)} 
     hidden_weights.each_index do |i| 
      hidden_weights[i].each_index do |j| 
       reversed[j][i] = hidden_weights[i][j]; 
      end 
     end 
     reversed 

    end 

    def calculate_hidden_error_gradients(output_error_gradients) 
     reversed = reversed_hidden_weights 
     hidden_nodes.each_with_index.map do |node, i| 
      Network::dsigmoid(hidden_nodes[i]) * output_error_gradients.zip(reversed[i]).map{|error, weight| error*weight}.inject(:+) 
     end 
    end 

    def update_all_weights(inputs, desired_results, hidden_error_gradients, output_error_gradients, learning_rate, momentum_rate) 
     update_weights(hidden_nodes, inputs, input_weights, hidden_error_gradients, learning_rate, previous_input_weight_deltas, momentum_rate) 
     update_weights(output_nodes, hidden_nodes, hidden_weights, output_error_gradients, learning_rate, previous_hidden_weight_deltas, momentum_rate) 
    end 

    def update_weights(nodes, values, weights, gradients, learning_rate, previous_deltas, momentum_rate) 
     weights.each_index do |i| 
      weights[i].each_index do |j| 
       delta = learning_rate * gradients[i] * values[j] 
       weights[i][j] += delta + momentum_rate * previous_deltas[i][j] 
       previous_deltas[i][j] = delta 
      end 
     end 


    end 

end 

end 

test.rb:

#!/usr/bin/ruby 

load "network.rb" 

learning_rate = 0.3 
momentum_rate = 0.2 

nn = Neural::Network.new(:inputs => 2, :hidden_nodes => 3, :output_nodes => 1) 
10000.times do |i| 
    # XOR - doesn't work 
    nn.train([0, 0], [0], learning_rate, momentum_rate) 
    nn.train([1, 0], [1], learning_rate, momentum_rate) 
    nn.train([0, 1], [1], learning_rate, momentum_rate) 
    nn.train([1, 1], [0], learning_rate, momentum_rate) 

    # AND - very rarely works 
    # nn.train([0, 0], [0], learning_rate, momentum_rate) 
    # nn.train([1, 0], [0], learning_rate, momentum_rate) 
    # nn.train([0, 1], [0], learning_rate, momentum_rate) 
    # nn.train([1, 1], [1], learning_rate, momentum_rate) 

    # OR - works 
    # nn.train([0, 0], [0], learning_rate, momentum_rate) 
    # nn.train([1, 0], [1], learning_rate, momentum_rate) 
    # nn.train([0, 1], [1], learning_rate, momentum_rate) 
    # nn.train([1, 1], [1], learning_rate, momentum_rate) 
end 

puts "--- TESTING ---" 
puts "[0, 0]" 
puts "result "+nn.predict([0, 0]).to_s 
puts 
puts "[1, 0]" 
puts "result "+nn.predict([1, 0]).to_s 
puts 
puts "[0, 1]" 
puts "result "+nn.predict([0, 1]).to_s 
puts 
puts "[1, 1]" 
puts "result "+nn.predict([1, 1]).to_s 
puts 
+0

मैं एक पूर्ण परीक्षण केस स्थापित करके डिबगिंग शुरू करूंगा जिसमें प्रारंभिक वजन और डेरिवेटिव/त्रुटियों को दो उदाहरणों के लिए शामिल किया गया है, फिर कोड में कदम उठाएं। – Motasim

+2

आपको उस समस्या को समझाते हुए * न्यूनतम * बिट कोड को वास्तव में कम करना चाहिए। आपके पास जो कुछ है वह काफी संपूर्ण एप्लिकेशन है। – tadman

+1

आपका प्रोग्राम ऐसा लगता है कि यह कुछ दिलचस्प कर रहा है। लेकिन प्रत्येक भाषा का अपना सम्मेलन होता है, और रूबी की शक्ति का हिस्सा इसकी समानता है। आप इस सवाल को codereview.stackexchange.com पर ला सकते हैं। –

उत्तर

13

मेरा जवाब होगा नहीं रूबी के बारे में है, लेकिन तंत्रिका नेटवर्क के बारे में। सबसे पहले, आपको समझना होगा कि अपने इनपुट और अपने नेटवर्क को पेपर पर कैसे लिखना है। यदि आप द्विआधारी ऑपरेटो को लागू करते हैं, तो आपकी जगह XY-plane पर चार बिंदुएं रखेगी। एक्स और वाई अक्ष पर सही और गलत चिह्नित करें और अपने चार अंक खींचें। यदि आप इसे सही करते हैं, तो आपको http://drawsave.com/1Tj

अब (शायद आपको न्यूरॉन की व्याख्या नहीं पता था) एक विमान पर एक रेखा के रूप में न्यूरॉन आकर्षित करने का प्रयास करें, जो आपके अंक को आपकी आवश्यकता के अनुसार अलग करता है। उदाहरण के लिए, यह और: enter image description here की रेखा है, रेखा गलत उत्तरों को गलत से अलग करती है। यदि आप समझते हैं, तो आप OR के लिए लाइन लिख सकते हैं। एक्सओआर एक परेशानी होगी।

और इस डीबग के अंतिम चरण के रूप में, एक न्यूरॉन को एक रेखा के रूप में महसूस करें। इसके बारे में एक साहित्य खोजें, मुझे याद नहीं है कि मौजूदा लाइन से न्यूरॉन कैसे बनाया जाए। यह वास्तव में सरल होगा। फिर इसके लिए एक न्यूरॉन वेक्टर बनाएं और इसे कार्यान्वित करें। एक न्यूरॉन नेटवर्क के रूप में महसूस करें, जहां न्यूरॉन को आपके और आपके पेपर पर गणना की जाती है। यदि आप सब सही करते हैं, तो आपका नेटवर्क करेगा और कार्य करेगा। मैंने इतनी बड़ी संख्या में पत्र लिखे हैं क्योंकि आप किसी कार्य को समझने से पहले एक प्रोग्राम लिखते हैं। मैं किसी न किसी तरह का होना नहीं चाहता, लेकिन एक्सओआर के आपके उल्लेख ने इसे दिखाया। यदि आप एक न्यूरॉन पर एक्सओआर बनाने की कोशिश करेंगे, तो आपको कुछ भी नहीं मिलेगा - गलत उत्तरों को गलत से अलग करना असंभव है। किताबों में इसे "एक्सओआर रैखिक अलग नहीं है" कहा जाता है। तो एक्सओआर के लिए आपको दो परत नेटवर्क बनाने की जरूरत है। उदाहरण के लिए, आपके पास पहली परत के रूप में और एक और परत के रूप में और नहीं होगा।

यदि आप अभी भी इसे पढ़ते हैं और आप जो मैंने लिखा है उसे समझते हैं, तो आपको डिबगिंग नेटवर्क के साथ कोई परेशानी नहीं होगी। यदि आपका नेटवर्क कुछ फ़ंक्शन सीखने में विफल रहता है, तो इसे एक पेपर पर बनाएं, फिर अपने नेटवर्क को हार्डकोड करें और इसका परीक्षण करें। यदि यह अभी भी विफल रहता है, तो आप इसे एक पेपर पर गलत बनाते हैं - मेरे व्याख्यान को दोबारा पढ़ें;)

+0

ऐसा लगता है कि प्रश्न में कोड में दो परतों में चार नोड्स व्यवस्थित हैं: तीन नोड्स की एक मध्यम परत, और एक नोड की आउटपुट परत। –

+0

ठीक है, धन्यवाद, मैंने कोड को ध्यान से नहीं पढ़ा। तो त्वरित उत्तर हो सकता है: मध्यम परत को दो नोड्स से बनाने का प्रयास करें और अपने एल्गोरिदम को दोबारा शुरू करें। –

+1

यह स्वीकार्य उत्तर होना चाहिए। – garbagecollector

0

मुझे एक ही समस्या थी और जवाब है - सीखने की गति के उच्च मूल्यों का उपयोग करें। मैं निम्नलिखित lSpeed = 12.8/epoch और 100 epoches एनएन के लिए phi(x) = x/(1 + |x|)

संभवतः अब आपकी एनएन सीखने की गति में नौकरी बनाने के लिए पर्याप्त "शक्ति" नहीं है।

0

यदि आप न्यूरोइवल्यूशन पर विचार करना चाहते हैं, तो आप neuroevo मणि देख सकते हैं।चश्मा भागो यह 15 पुनरावृत्तियों ([2,2,1] फ़ीड आगे नेटवर्क, XNES अनुकूलक) में XOR फिट देखने के लिए:

https://github.com/giuse/neuroevo/blob/master/spec/solver_spec.rb

पूर्ण प्रकटीकरण: मैं डेवलपर हूं (हाय वहाँ!)।
मैंने हाल ही में अपना कोड प्रकाशित करना शुरू कर दिया है और प्रतिक्रिया की तलाश में हूं।

संबंधित मुद्दे