反向传播实施问题
Posted
技术标签:
【中文标题】反向传播实施问题【英文标题】:Backprop implementation issue 【发布时间】:2011-05-04 17:16:25 【问题描述】:我应该做什么。我有一张黑白图片(100x100px):
我应该用这张图片训练一个backpropagation 神经网络。输入是图像的 x、y 坐标(从 0 到 99),输出是 1(白色)或 0(黑色)。
一旦网络学会了,我希望它根据权重再现图像,并获得最接近原始图像的可能图像。
这是我的反向传播实现:
import os
import math
import Image
import random
from random import sample
#------------------------------ class definitions
class Weight:
def __init__(self, fromNeuron, toNeuron):
self.value = random.uniform(-0.5, 0.5)
self.fromNeuron = fromNeuron
self.toNeuron = toNeuron
fromNeuron.outputWeights.append(self)
toNeuron.inputWeights.append(self)
self.delta = 0.0 # delta value, this will accumulate and after each training cycle used to adjust the weight value
def calculateDelta(self, network):
self.delta += self.fromNeuron.value * self.toNeuron.error
class Neuron:
def __init__(self):
self.value = 0.0 # the output
self.idealValue = 0.0 # the ideal output
self.error = 0.0 # error between output and ideal output
self.inputWeights = []
self.outputWeights = []
def activate(self, network):
x = 0.0;
for weight in self.inputWeights:
x += weight.value * weight.fromNeuron.value
# sigmoid function
if x < -320:
self.value = 0
elif x > 320:
self.value = 1
else:
self.value = 1 / (1 + math.exp(-x))
class Layer:
def __init__(self, neurons):
self.neurons = neurons
def activate(self, network):
for neuron in self.neurons:
neuron.activate(network)
class Network:
def __init__(self, layers, learningRate):
self.layers = layers
self.learningRate = learningRate # the rate at which the network learns
self.weights = []
for hiddenNeuron in self.layers[1].neurons:
for inputNeuron in self.layers[0].neurons:
self.weights.append(Weight(inputNeuron, hiddenNeuron))
for outputNeuron in self.layers[2].neurons:
self.weights.append(Weight(hiddenNeuron, outputNeuron))
def setInputs(self, inputs):
self.layers[0].neurons[0].value = float(inputs[0])
self.layers[0].neurons[1].value = float(inputs[1])
def setExpectedOutputs(self, expectedOutputs):
self.layers[2].neurons[0].idealValue = expectedOutputs[0]
def calculateOutputs(self, expectedOutputs):
self.setExpectedOutputs(expectedOutputs)
self.layers[1].activate(self) # activation function for hidden layer
self.layers[2].activate(self) # activation function for output layer
def calculateOutputErrors(self):
for neuron in self.layers[2].neurons:
neuron.error = (neuron.idealValue - neuron.value) * neuron.value * (1 - neuron.value)
def calculateHiddenErrors(self):
for neuron in self.layers[1].neurons:
error = 0.0
for weight in neuron.outputWeights:
error += weight.toNeuron.error * weight.value
neuron.error = error * neuron.value * (1 - neuron.value)
def calculateDeltas(self):
for weight in self.weights:
weight.calculateDelta(self)
def train(self, inputs, expectedOutputs):
self.setInputs(inputs)
self.calculateOutputs(expectedOutputs)
self.calculateOutputErrors()
self.calculateHiddenErrors()
self.calculateDeltas()
def learn(self):
for weight in self.weights:
weight.value += self.learningRate * weight.delta
def calculateSingleOutput(self, inputs):
self.setInputs(inputs)
self.layers[1].activate(self)
self.layers[2].activate(self)
#return round(self.layers[2].neurons[0].value, 0)
return self.layers[2].neurons[0].value
#------------------------------ initialize objects etc
inputLayer = Layer([Neuron() for n in range(2)])
hiddenLayer = Layer([Neuron() for n in range(10)])
outputLayer = Layer([Neuron() for n in range(1)])
learningRate = 0.4
network = Network([inputLayer, hiddenLayer, outputLayer], learningRate)
# let's get the training set
os.chdir("D:/stuff")
image = Image.open("backprop-input.gif")
pixels = image.load()
bbox = image.getbbox()
width = 5#bbox[2] # image width
height = 5#bbox[3] # image height
trainingInputs = []
trainingOutputs = []
b = w = 0
for x in range(0, width):
for y in range(0, height):
if (0, 0, 0, 255) == pixels[x, y]:
color = 0
b += 1
elif (255, 255, 255, 255) == pixels[x, y]:
color = 1
w += 1
trainingInputs.append([float(x), float(y)])
trainingOutputs.append([float(color)])
print "\nOriginal image ... Black:"+str(b)+" White:"+str(w)+"\n"
#------------------------------ let's train
for i in range(500):
for j in range(len(trainingOutputs)):
network.train(trainingInputs[j], trainingOutputs[j])
network.learn()
for w in network.weights:
w.delta = 0.0
#------------------------------ let's check
b = w = 0
for x in range(0, width):
for y in range(0, height):
out = network.calculateSingleOutput([float(x), float(y)])
if 0.0 == round(out):
color = (0, 0, 0, 255)
b += 1
elif 1.0 == round(out):
color = (255, 255, 255, 255)
w += 1
pixels[x, y] = color
#print out
print "\nAfter learning the network thinks ... Black:"+str(b)+" White:"+str(w)+"\n"
显然,我的实现存在一些问题。以上代码返回:
原始图像...黑色:21 白色:4
学习后网络认为... 黑色:25 白色:0
如果我尝试使用更大的训练集,它会做同样的事情(出于测试目的,我只测试上图中的 25 个像素)。它返回学习后所有像素都应该是黑色的。
现在,如果我改用这样的手动训练集:
trainingInputs = [
[0.0,0.0],
[1.0,0.0],
[2.0,0.0],
[0.0,1.0],
[1.0,1.0],
[2.0,1.0],
[0.0,2.0],
[1.0,2.0],
[2.0,2.0]
]
trainingOutputs = [
[0.0],
[1.0],
[1.0],
[0.0],
[1.0],
[0.0],
[0.0],
[0.0],
[1.0]
]
#------------------------------ let's train
for i in range(500):
for j in range(len(trainingOutputs)):
network.train(trainingInputs[j], trainingOutputs[j])
network.learn()
for w in network.weights:
w.delta = 0.0
#------------------------------ let's check
for inputs in trainingInputs:
print network.calculateSingleOutput(inputs)
输出例如:
0.0330125791296 # this should be 0, OK
0.953539182136 # this should be 1, OK
0.971854575477 # this should be 1, OK
0.00046146137467 # this should be 0, OK
0.896699762781 # this should be 1, OK
0.112909223162 # this should be 0, OK
0.00034058462280 # this should be 0, OK
0.0929886299643 # this should be 0, OK
0.940489647869 # this should be 1, OK
换句话说,网络猜对了所有像素(黑色和白色)。如果我使用图像中的实际像素而不是像上面这样的硬编码训练集,为什么它说所有像素都应该是黑色的?
我尝试更改隐藏层中的神经元数量(最多 100 个神经元),但没有成功。
这是一个家庭作业。
这也是我previous question关于反向传播的延续。
【问题讨论】:
你为什么把它标记为 MATLAB?看起来你只使用 Python。 @gnovice 好吧,我认为 MATLAB 经常用于编程神经网络和其他 AI 东西,所以我认为一些 MATLAB 程序员可能能够发现我的算法中的错误,即使它是用 Python 编写的. 【参考方案1】:已经有一段时间了,但我确实获得了这方面的学位,所以我想希望其中一些已经停滞不前。
据我所知,您的输入集使您的中间层神经元严重超载。也就是说,您的输入集包含 10,000 个离散输入值(100 pix x 100 pix);您正在尝试将这 10,000 个值编码为 10 个神经元。这种级别的编码很难(我怀疑这是可能的,但肯定很难);至少,您需要大量训练(超过 500 次运行)才能使其合理重现。即使中间层有 100 个神经元,您仍会看到一个相对密集的压缩级别(100 个像素到 1 个神经元)。
至于如何处理这些问题;嗯,这很棘手。你可以显着增加你的中间神经元的数量,你会得到一个合理的效果,但当然需要很长时间来训练。但是,我认为可能有不同的解决方案;如果可能,您可以考虑使用极坐标而不是笛卡尔坐标作为输入;快速观察输入模式表明高度对称,实际上你会看到一个线性模式,沿着角坐标具有重复的可预测变形,它似乎可以在少数中间层神经元中很好地编码。
这东西很棘手;寻求模式编码的通用解决方案(就像您的原始解决方案一样)非常复杂,并且通常(即使有大量中间层神经元)需要大量的训练通道;另一方面,一些高级启发式任务分解和一些问题重新定义(即提前从笛卡尔坐标转换为极坐标)可以为明确定义的问题集提供良好的解决方案。当然,其中是永恒的摩擦。一般的解决方案很难得到,但稍微具体一点的解决方案确实很好。
有趣的东西,无论如何!
【讨论】:
@Amro:谢谢,对称性非常清楚地适用于极坐标。 @McWafflestix:在解决机器学习问题中,最重要的是有有用的特征(预处理步骤),算法考虑其次(你通常可以使用某种交叉验证来找到模型的最佳参数) @McWafflestix 感谢您的建议。我会试试看,但要到周末。我真的很忙。 @RichardKnop:没问题,很高兴为您提供帮助。请告诉我们事情的结果! @Amro:完全正确。 OP 表示输入是笛卡尔坐标;这就是为什么我对极坐标有点不确定的原因。如果对问题的约束只是输入必须是对偶的(即,笛卡尔坐标或极坐标),那么极坐标将是一个大问题,因为线性变换最有效地使用了给定输入的特征。以上是关于反向传播实施问题的主要内容,如果未能解决你的问题,请参考以下文章