Ruby 简单神经网络可以';不学习异或
我试图学习神经网络,并编写了一个简单的反向传播神经网络,它使用sigmoid激活函数、随机权重初始化和学习/梯度动量 当配置为2个输入、2个隐藏节点和1时,它无法学习XOR和and。但是,它将正确地学习或使用 我看不出我做错了什么,所以任何帮助都将不胜感激 谢谢 编辑:如上所述,我使用2个隐藏节点进行了测试,但下面的代码显示了3个节点的配置。在使用3个隐藏节点运行测试后,我只是忘记将其改回2 network.rb:Ruby 简单神经网络可以';不学习异或,ruby,machine-learning,neural-network,Ruby,Machine Learning,Neural Network,我试图学习神经网络,并编写了一个简单的反向传播神经网络,它使用sigmoid激活函数、随机权重初始化和学习/梯度动量 当配置为2个输入、2个隐藏节点和1时,它无法学习XOR和and。但是,它将正确地学习或使用 我看不出我做错了什么,所以任何帮助都将不胜感激 谢谢 编辑:如上所述,我使用2个隐藏节点进行了测试,但下面的代码显示了3个节点的配置。在使用3个隐藏节点运行测试后,我只是忘记将其改回2 network.rb: module Neural class Network attr_a
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
我的答案不是关于ruby,而是关于神经网络。 首先,你必须了解如何在纸上写下你的输入和你的网络。如果实现二进制运算符,则空间将由XY平面上的四个点组成。在X轴和Y轴上标记“真”和“假”,并绘制四个点。如果你做对了,你会得到这样的东西 现在(也许你不知道神经元的这种解释)试着把神经元画成平面上的一条线,根据你的需要分开你的点。例如,这是和的行: 该行将正确答案与错误答案分开。如果您理解,您可以为或写一行。XOR将是一个麻烦 作为调试的最后一步,将神经元实现为一条线。找一篇关于它的文献,我不记得如何通过现有的线路来构建神经元。这将是简单的,真的。然后为其构建一个神经元向量并实现它。实现和作为一个单神经元网络,其中神经元被定义为和,在纸上计算。如果你做的一切都正确,你的网络就会正常运作。 我写了这么多的信,只是因为你在理解任务之前写了一个程序。我不想粗鲁,但你提到XOR就说明了这一点。如果你试图在一个神经元上构建XOR,你将一无所获——不可能区分正确答案和错误答案。在书中它被称为“XOR不是线性可分的”。因此,对于XOR,您需要构建一个两层网络。例如,您将具有AND和not或作为第一层和作为第二层
如果您仍然阅读本文并且理解我写的内容,那么调试网络就不会有任何问题。如果您的网络无法学习某些功能,请将其构建在纸上,然后对网络进行硬编码并进行测试。如果仍然失败,你就把它建立在一篇不正确的论文上——重读我的讲座;) 我也有同样的问题,答案是——使用更高的学习速度值。对于具有
phi(x)=x/(1+| x |)
可能现在你的NN学习速度没有足够的“力量”来完成这项工作。 < P>如果你想考虑神经进化,你可以检查<代码> NoeEvo < /C>宝石。运行规范,查看它在15次迭代中是否适合XOR(前馈网络,
XNES
optimizer):
完全披露:我是开发者(大家好!)。我最近刚开始发布我的代码,正在寻找反馈。我将通过设置一个完整的测试用例来开始调试,其中包括两个示例的初始权重和导数/错误,然后进入代码。您应该将其减少到解释问题的代码的最小部分。你这里的几乎是一个完整的应用程序。你的程序看起来可能正在做一些有趣的事情。但是每种语言都有自己的约定,Ruby的一部分力量在于它的简洁性。你可能会把这个问题带到codereview.stackexchange.com.Comments上的类似帖子上,以便查看OP的代码,所以我决定发布我的整个应用程序。然而,我同意,这是冗长的,坦率地说,我对这些数字或答复感到惊讶。在发布之前,我浏览了一个测试用例,并在反向传播的每个步骤中验证了计算值是否与预期值匹配。我将尝试使用其他测试用例,并阅读更多关于BP的内容,以了解我是否误解了该算法。这段代码似乎有四个节点分为两层:中间层有三个节点,输出层有一个节点。好的,谢谢,我没有仔细阅读代码。所以快速的答案可能是:尝试从两个节点创建中间层,然后重新运行您的算法。