帮助我在 Python 中实现反向传播

2024-05-02

EDIT2:

新的训练集...

Inputs:

[
 [0.0, 0.0], 
 [0.0, 1.0], 
 [0.0, 2.0], 
 [0.0, 3.0], 
 [0.0, 4.0], 
 [1.0, 0.0], 
 [1.0, 1.0], 
 [1.0, 2.0], 
 [1.0, 3.0], 
 [1.0, 4.0], 
 [2.0, 0.0], 
 [2.0, 1.0], 
 [2.0, 2.0], 
 [2.0, 3.0], 
 [2.0, 4.0], 
 [3.0, 0.0], 
 [3.0, 1.0], 
 [3.0, 2.0], 
 [3.0, 3.0], 
 [3.0, 4.0],
 [4.0, 0.0], 
 [4.0, 1.0], 
 [4.0, 2.0], 
 [4.0, 3.0], 
 [4.0, 4.0]
]

Outputs:

[
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [1.0], 
 [1.0], 
 [0.0], 
 [0.0], 
 [0.0], 
 [1.0], 
 [1.0]
]

EDIT1:

我已经用我的最新代码更新了问题。我修复了一些小问题,但在网络学习后,所有输入组合仍然得到相同的输出。

这是反向传播算法的解释:反向传播算法 http://mihaiv.wordpress.com/2010/02/08/backpropagation-algorithm/


是的,这是一项作业,一开始就要把这一点说清楚。

我应该在一个简单的神经网络上实现一个简单的反向传播算法。

我选择 Python 作为此任务的首选语言,并且选择了如下所示的神经网络:

3 层:1 个输入层、1 个隐藏层、1 个输出层:

O         O

                    O

O         O

输入神经元上有一个整数,输出神经元上有 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(100)])
outputLayer = Layer([Neuron() for n in range(1)])

learningRate = 0.5

network = Network([inputLayer, hiddenLayer, outputLayer], learningRate)

# just for debugging, the real training set is much larger
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()

#------------------------------ let's check


for pattern in trainingInputs:
    print network.calculateSingleOutput(pattern)

现在的问题是,在学习之后,网络似乎为所有输入组合返回一个非常接近 0.0 的浮点数,即使是那些应该接近 1.0 的组合。

我训练网络 100 个周期,每个周期我都会做:

对于训练集中的每组输入:

  • 设置网络输入
  • 使用 sigmoid 函数计算输出
  • 计算输出层的误差
  • 计算隐藏层的误差
  • 计算权重增量

然后我根据学习率和累积增量调整权重。

这是我的神经元激活函数:

def activationFunction(self, network):
    """
    Calculate an activation function of a neuron which is a sum of all input weights * neurons where those weights start
    """
    x = 0.0;
    for weight in self.inputWeights:
        x += weight.value * weight.getFromNeuron(network).value
    # sigmoid function
    self.value = 1 / (1 + math.exp(-x))

这是我计算增量的方法:

def calculateDelta(self, network):
    self.delta += self.getFromNeuron(network).value * self.getToNeuron(network).error

这是我的算法的一般流程:

for i in range(numberOfIterations):
    for k,expectedOutput in trainingSet.iteritems():
        coordinates = k.split(",")
        network.setInputs((float(coordinates[0]), float(coordinates[1])))
        network.calculateOutputs([float(expectedOutput)])
        network.calculateOutputErrors()
        network.calculateHiddenErrors()
        network.calculateDeltas()
    oldWeights = network.weights
    network.adjustWeights()
    network.resetDeltas()
    print "Iteration ", i
    j = 0
    for weight in network.weights:
        print "Weight W", weight.i, weight.j, ": ", oldWeights[j].value, " ............ Adjusted value : ", weight.value
        j += j

输出的最后两行是:

0.552785449458 # this should be close to 1
0.552785449458 # this should be close to 0

它实际上返回所有输入组合的输出数。

我错过了什么吗?


看起来你得到的几乎是神经元的初始状态(几乎self.idealValue)。也许您不应该在提供实际数据之前初始化这个神经元?

EDIT:好的,我更深入地研究了代码并对其进行了一些简化(将在下面发布简化版本)。基本上你的代码有两个小错误(看起来像是你刚刚忽略的事情),但这会导致网络肯定无法工作。

  • 在学习阶段,您忘记在输出层中设置预期输出的值。如果没有这个,网络肯定无法学习任何东西,并且将始终停留在初始的 IdealValue 上。 (这是我在第一次阅读时发现的行为)。这个甚至可能在您对训练步骤的描述中被发现(如果您没有发布代码,则可能会出现这种情况,这是我知道的罕见情况之一,实际发布代码是隐藏错误而不是制造错误明显的)。您在 EDIT1 之后修复了这个问题。
  • 在calculateSingleOutputs中激活网络时,您忘记激活隐藏层。

显然,这两个问题中的任何一个都会导致网络功能失调。

一旦更正,它就可以工作(嗯,它在我的代码的简化版本中有效)。

这些错误不容易被发现,因为最初的代码太复杂了。在引入新类或新方法之前你应该三思而后行。没有创建足够的方法或类将使代码难以阅读和维护,但创建太多可能会使其更难以阅读和维护。你必须找到合适的平衡点。我个人找到这种平衡的方法是:代码异味 http://wiki.java.net/bin/view/People/SmellsToRefactorings以及重构技术,无论它们引导我到哪里。有时添加方法或创建类,有时删除它们。它当然不完美,但这对我有用。

下面是我应用一些重构后的代码版本。我花了大约一个小时更改您的代码,但始终保持其功能等效。我认为这是一次很好的重构练习,因为最初的代码读起来真的很糟糕。重构后只用了5分钟就发现了问题。

import os
import math

"""
A simple backprop neural network. It has 3 layers:
    Input layer: 2 neurons
    Hidden layer: 2 neurons
    Output layer: 1 neuron
"""

class Weight:
    """
    Class representing a weight between two neurons
    """
    def __init__(self, value, from_neuron, to_neuron):
        self.value = value
        self.from_neuron = from_neuron
        from_neuron.outputWeights.append(self)
        self.to_neuron = to_neuron
        to_neuron.inputWeights.append(self)

        # delta value, this will accumulate and after each training cycle
        # will be used to adjust the weight value
        self.delta = 0.0

class Neuron:
    """
    Class representing a 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 = []    # weights that end in the neuron
        self.outputWeights = []  # weights that starts in the neuron

    def activate(self):
        """
        Calculate an activation function of a neuron which is 
        a sum of all input weights * neurons where those weights start
        """
        x = 0.0;
        for weight in self.inputWeights:
            x += weight.value * weight.from_neuron.value
        # sigmoid function
        self.value = 1 / (1 + math.exp(-x))

class Network:
    """
    Class representing a whole neural network. Contains layers.
    """
    def __init__(self, layers, learningRate, weights):
        self.layers = layers
        self.learningRate = learningRate    # the rate at which the network learns
        self.weights = weights

    def training(self, entries, expectedOutput):
        for i in range(len(entries)):
            self.layers[0][i].value = entries[i]
        for i in range(len(expectedOutput)):
            self.layers[2][i].idealValue = expectedOutput[i]
        for layer in self.layers[1:]:
            for n in layer:
                n.activate()
        for n in self.layers[2]:
            error = (n.idealValue - n.value) * n.value * (1 - n.value)
            n.error = error
        for n in self.layers[1]:
            error = 0.0
            for w in n.outputWeights:
                error += w.to_neuron.error * w.value
            n.error = error
        for w in self.weights:
            w.delta += w.from_neuron.value * w.to_neuron.error

    def updateWeights(self):
        for w in self.weights:
            w.value += self.learningRate * w.delta

    def calculateSingleOutput(self, entries):
        """
        Calculate a single output for input values.
        This will be used to debug the already learned network after training.
        """
        for i in range(len(entries)):
            self.layers[0][i].value = entries[i]
        # activation function for output layer
        for layer in self.layers[1:]:
            for n in layer:
                n.activate()
        print self.layers[2][0].value


#------------------------------ initialize objects etc

neurons = [Neuron() for n in range(5)]

w1 = Weight(-0.79, neurons[0], neurons[2])
w2 = Weight( 0.51, neurons[0], neurons[3])
w3 = Weight( 0.27, neurons[1], neurons[2])
w4 = Weight(-0.48, neurons[1], neurons[3])
w5 = Weight(-0.33, neurons[2], neurons[4])
w6 = Weight( 0.09, neurons[3], neurons[4])

weights = [w1, w2, w3, w4, w5, w6]
inputLayer  = [neurons[0], neurons[1]]
hiddenLayer = [neurons[2], neurons[3]]
outputLayer = [neurons[4]]
learningRate = 0.3
network = Network([inputLayer, hiddenLayer, outputLayer], learningRate, weights)

# just for debugging, the real training set is much larger
trainingSet = [([0.0,0.0],[0.0]),
               ([1.0,0.0],[1.0]),
               ([2.0,0.0],[1.0]),
               ([0.0,1.0],[0.0]),
               ([1.0,1.0],[1.0]),
               ([2.0,1.0],[0.0]),
               ([0.0,2.0],[0.0]),
               ([1.0,2.0],[0.0]),
               ([2.0,2.0],[1.0])]

#------------------------------ let's train
for i in range(100): # training iterations
    for entries, expectedOutput in trainingSet:
        network.training(entries, expectedOutput)
    network.updateWeights()

#network has learned, let's check
network.calculateSingleOutput((1, 0)) # this should be close to 1
network.calculateSingleOutput((0, 0)) # this should be close to 0

顺便说一句,还有第三个问题我没有纠正(但很容易纠正)。如果 x 太大或太小(> 320 或 math.exp()会引发异常。如果您申请训练迭代(例如几千次),就会发生这种情况。我看到的最简单的纠正方法是检查 x 的值,如果它太大或太小,则根据情况将 Neuron 的值设置为 0 或 1,这是极限值。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

帮助我在 Python 中实现反向传播 的相关文章

随机推荐

  • 在java中读取文本文件[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 当每行都包含整数 字符串和双精度数时 如何在 Java 中读取 txt 文件并将每一行放入数组中 每行都有不同数量的单词 数字 Try
  • 如何隐藏实际的下载文件夹位置

    我想隐藏下载文件夹位置 以便用户下载文件时看不到该位置 我认为这可以使用 htaccess 文件来完成 但我该如何做到这一点 或者如何使用 PHP 来完成此操作 我在 PHP 中是这样做的
  • 全屏 Exoplayer

    我尝试用以下内容显示节目视频 mp4 外播放器 in 回收视图 and 浏览器 我展示了具有自定义布局的视频控制器 到目前为止 一切都很好 现在尝试像其他视频播放器一样全屏播放视频 但在中找不到好方法外播放器 doc 谁能帮我 ExoPla
  • iphone - UIScrollview - 带有慢速动画的scrollRectToVisible

    我正在使用 UIScrollView 并使用scrollRectToVisible animated 这对我来说效果很好 但我想慢慢滚动到一个位置 以便用户可以注意到效果 是否可以 我正在尝试以下代码 但没有成功 UIView beginA
  • 使用 NSPredicate 进行过滤,用于数组内字典内数组的数组计数

    我有如下格式的数组 xyz Array with different values many more keys same as above dictionary many more dictionaries 在这里看 我有字典的主数组 其
  • Scrapy文件下载如何使用自定义文件名

    For my scrapy http doc scrapy org index html我目前正在使用的项目文件管道 https doc scrapy org en latest topics media pipeline html scr
  • Rails Beta 请求注册并提供社交媒体分享奖励

    我想构建一个简单的测试版请求注册页面 当用户尽可能多地共享应用程序的链接时 用户将获得更早的奖励 这样的解决方案可以在 特伦维网站 用户输入电子邮件 用户通过其独特的代码获得独特的链接 用户在每次注册时分享此链接 这对他来说是 1 管理方法
  • 对于没有固定/相对/绝对位置的元素,是否有 z-index 替代方案?

    我需要在更高的位置显示一个元素 z level 问题是 该元素位于带有 a 的 div 内 display flex and justify content space around 正常的z index属性不起作用 我认为这是因为该元素没
  • 为什么我在模拟器中看不到视频?

    我见过几个与此类似的问题 但我想确定一下 我无法在模拟器上运行视频 是否一致 有人在模拟器上成功运行视频吗 以下是我使用的代码 import android app Activity import android net Uri impor
  • 使用相同的 SqlConnection 对 SqlCommand.BeginExecuteNonQuery 进行多个并发调用

    我有一些可用的 C 代码 它使用 SqlConnection 创建临时表 例如 Foo 调用存储过程来填充这些临时表并将结果返回到 C 客户端 使用 C 对这些结果执行复杂的计算 并使用计算结果更新之前创建的临时表之一 由于整个过程中都会使
  • 防止IndexedDB请求错误取消事务

    我的意图 循环localStorage并将数据放入IndexedDB 如果发生某些已知错误 例如当键已存在时出现 ConstraintError 我想忽略这些特定错误 以便事务不会中止 当请求触发错误时 中止事务是默认行为 问题 我以为使用
  • 为什么 JavaScript 在不同浏览器中不一致?

    在花了无数个小时修复 JS 以使其跨浏览器兼容 主要是 IE 之后 我一直在思考以下问题 Why不是 JavaScript持续的跨浏览器 我的意思是 为什么 JS 不能像 Java 和 Flash 那样好呢 相反 我们必须求助于 jQuer
  • 避免调用成员变量的构造函数

    我有以下 C 类 Header File class A public A private B m B C m C cpp File A A m B 1 m B doSomething m B doMore m C C m B getSom
  • SQL CE Compact 3.5 表的标识列

    是否有一个查询可以针对 INFORMATION SCHEMA 或针对系统表编写 以确定某个列是否是 SQL CE 版本 3 5 中的标识列 Use 列属性 供您参考 a 列属性 Transact SQL http technet micro
  • 遍历内存编辑每个字节

    我正在编写汇编代码 提示用户输入一串小写字符 然后输出包含所有大写字符的相同字符串 我的想法是迭代从特定地址开始的字节 并从每个字节中减去 20H 将小写变为大写 直到到达具有特定值的字节 我对 Assembly 相当缺乏经验 所以我不确定
  • 尝试输入字符串时出现名称错误[重复]

    这个问题在这里已经有答案了 import pickle import os import time class Person def init self number address self number number self addr
  • 使用 cmake 和 opencv 对符号“gzclose”的未定义引用[关闭]

    Closed 这个问题是无法重现或由拼写错误引起 help closed questions 目前不接受答案 我尝试构建该项目 doppia https bitbucket org rodrigob doppia 但发生链接错误 我想这是一
  • java.lang.NullPointerException(无错误消息)APK构建

    Top level build file where you can add configuration options common to all sub projects modules buildscript repositories
  • 如何在 Mulesoft 中将睡眠设置为流程而不丢失消息负载

    我想插入脚本来延迟 Mulesoft 中的处理流程 我尝试在 groovy 中插入脚本 但丢失了消息有效负载 因此当我必须获取消息有效负载时 收到了空指针 我怎样才能不丢失消息有效负载 Thanks 如果您正在使用Groovy流程中的组件
  • 帮助我在 Python 中实现反向传播

    EDIT2 新的训练集 Inputs 0 0 0 0 0 0 1 0 0 0 2 0 0 0 3 0 0 0 4 0 1 0 0 0 1 0 1 0 1 0 2 0 1 0 3 0 1 0 4 0 2 0 0 0 2 0 1 0 2 0 2