6

私はニューラル ネットワークについて学習しようとしており、シグモイド活性化関数、ランダムな重みの初期化、および学習/勾配運動量を使用する単純な逆伝播ニューラル ネットワークをコーディングしました。

2 つの入力、2 つの隠しノード、および 1 つの構成の場合、XOR と AND の学習に失敗します。ただし、OR は正しく学習します。

私は自分が間違ったことを理解していないので、どんな助けも大歓迎です。

ありがとう

編集:述べたように、2 つの隠しノードでテストしましたが、以下のコードは 3 の構成を示しています。3 つの隠しノードを使用してテストを実行した後、これを 2 に戻すのを忘れていました。

ネットワーク.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
4

3 に答える 3

13

私の答えはルビーについてではなく、ニューラルネットワークについてです。まず、インプットとネットワークを紙に書く方法を理解する必要があります。バイナリ オペラトを実装すると、空間は XY 平面上の 4 つの点で構成されます。X 軸と Y 軸に true と false をマークし、4 つの点を描きます。あなたがそれを正しくすれば、あなたはこのようなものを受け取りますhttp://drawsave.com/1Tj

ここで (ニューロンのこの解釈を知らなかったかもしれません)、必要に応じてポイントを分離する平面上の線としてニューロンを描画してみてください。たとえば、これは AND の行です。ここに画像の説明を入力 この行は、正解と不正解を分けています。理解できれば、OR の行を書くことができます。XORは面倒です。

そして、このデバッグの最後のステップとして、ニューロンを線として実現します。それについての文献を見つけてください。既存の行でニューロンを構築する方法を覚えていません。本当に簡単です。次に、AND のニューロン ベクトルを作成し、それを実装します。AND を単一のニューロン ネットワークとして実現します。ここで、ニューロンは AND として定義され、紙の上で計算されます。すべて正しく行うと、ネットワークは AND 機能を実行します。タスクを理解する前にプログラムを書くという理由だけで、私は膨大な数の手紙を書きました。大まかに言いたくないのですが、XORについてのあなたの言及はそれを示しました。1 つのニューロンで XOR を構築しようとすると、何も得られません。正解と不正解を区別することは不可能です。本では「XORは線形分離可能ではない」と呼ばれています。したがって、XOR の場合、2 層ネットワークを構築する必要があります。例えば、

これをまだ読んでいて、私が書いたことを理解していれば、ネットワークのデバッグに問題はありません。ネットワークが機能の学習に失敗した場合は、それを紙の上に構築し、ネットワークをハードコーディングしてテストします。それでも失敗する場合は、間違った紙の上に構築します-私の講義を読み直してください;)

于 2012-12-22T12:17:12.073 に答える