Python 感知器神经网络不会学习特定范围内的值

Python 感知器神经网络不会学习特定范围内的值,python,python-3.x,neural-network,perceptron,Python,Python 3.x,Neural Network,Perceptron,我正在玩弄神经网络,想制作一个干净的类实现来处理任何大小的网络。目前,我正在调试我的学习功能来处理两层网络 在当前状态下,使用逻辑激活: 它无法学习低于0.5的值 它不能处理输入向量的矩阵(只有单个输入向量),这可以在以后实现 如果初始权重和偏差导致输出小于0.5,则可能会学习到接近0 假设:在“理想”条件下,它将使用二进制输入的任意组合学习0.5到1之间的任何值 这已通过2个和3个网络输入进行了测试 无论层数多少,是否正确进行正向传播 以下是相关代码: import numpy as

我正在玩弄神经网络,想制作一个干净的类实现来处理任何大小的网络。目前,我正在调试我的学习功能来处理两层网络

在当前状态下,使用逻辑激活:

  • 它无法学习低于0.5的值
  • 它不能处理输入向量的矩阵(只有单个输入向量),这可以在以后实现
  • 如果初始权重和偏差导致输出小于0.5,则可能会学习到接近0
  • 假设:在“理想”条件下,它将使用二进制输入的任意组合学习0.5到1之间的任何值
    • 这已通过2个和3个网络输入进行了测试
  • 无论层数多少,是否正确进行正向传播
以下是相关代码:

import numpy as np

def logistic(x, deriv = False):
  '''
  If using the derivative, input must be result of logistic
  '''
  if deriv:
    return x*(1-x)
    
  return 1/(1+np.exp(-x))

def feed_forw(input, weights):
  '''
  ***Wrapper for input.dot(weights)
  Input should be a np.array the same length as number of input nodes
    - A row of input represents the vector of input nodes
    - Different Rows are different input cases
  Weights is a 2D np.array of weights for each input node to each output node
    - dimensions of weights will determine length of output vector
    - top row is weights going from first input to node to all output nodes
    - first col is weights going from all input nodes to first output node
  '''

  return input.dot(weights)

class ANN:
  '''
  Artificial Neural Network of Perceptron Design
  Member Attributes:
    Weights: tuple of np.array
    - # of elements define number of layers
    - shapes of each element define nodes of each connecting pair of connecting layers
    Bias: tuple of np.array
    - added to each node after the first layer on a per layer basis
    - must have same dimensions as output from each corresponding element in Weights
    Target: np.array
    - array representing desired output.
  '''
  
  def __init__(self, weights, bias = 0, target = None):
    self._weights = weights
    self._bias = bias
    self._target = target

  def __str__(self):
    data = ''
    for w,b in zip(self._weights, self._bias):
      data += f'Weight\n{w}\nbias\n{b}\n'

    return f'{data}Seeking\n{self._target}\n'

  def _forwardProp(self, v, activation):
    '''
    Helper function to Learn
    '''
    out = []
    out.append(v.copy())
    for w,b in zip(self._weights, self._bias):
      out.append(feed_forw(out[-1], w) + b)
      out.append(activation(out[-1]))
    return out

  def setTarget(self, target):
    self._target = target

  def learn(self, input, activation, epoch = 10, eta = 1, debug = False):
    '''
    ***Currently only functions with 2-Layer perceptrons***
    input: np.array
    - a matrix representing each of case of input vectors
    - rows are input vectors for a single case
    activation: function object
    - An activation function used to normalize output
    epoch: int
    - test cycles
    eta: int
    - learning parameter
    '''
    for e in range(epoch):
      layers = self._forwardProp(input, activation)
      #layers is a list for keeping track of changes between layers
      #Pattern follows:
      #[input, layer 0 - weighted sum, layer 1 - activation, layer 1(output) - 
      #   weighted sum, layer 2 - activation, layer 2 ... 
      #   weighted sum, output layer - activation, ouput layer]
      
      #Final element is always network output
      error = layers[-1] - self._target

      delta_out = error * activation(layers[-1], deriv = True)
      #derivError_out = delta_out * activation(layers[-3].T*self._weights[-1])
      #derivError_out = delta_out * layers[-3].T*self._weights[-1]
      #EDIT
      derivError_out = delta_out * layers[-3].T
      derivError_bias = delta_out * self._bias[-1].T
      self._weights += -eta*derivError_out
      self._bias += -eta*derivError_bias

      if debug:
        print(f'Epoch {e+1}:\nOutput:\n{layers[-1]}\nError is\n{error}\nDelta Out Node:\n{delta_out}')
        print(f'Weight Increment:\n{derivError_out}\nBias Increment:\n{derivError_bias}')
        print(f'State after training rotation:\n{self}')

      #i = 1
      #while i < len(layers) + 1:
        #This loop will count from the last element of layers, will go back by 2
        #...
        #i += 2
我可以看到,当输出小于0.5时,出于某种原因,这将使输出降低,无论发生什么。如果起始输出小于0.5,它将只学习小于起始输出的值。如果起始输出为0.5或更大,它将只学习一个也大于0.5的值。然而,我仍然无法找到解决这个问题的方法(至少是优雅地)

这是两个争用的案例,所以我可以强行修复。但是,我不会知道我犯了什么错误

我知道有多种方法来实现这个网络,甚至存在这个可笑的简单变体,我仍然无法理解它的数学。但是,在做了几周这件事之后,我只能假设这是一个我没能看到的小错误

此编辑向解决方案迈出了一步。 在类定义中,更改了以下行

#derivError_out = delta_out * activation(layers[-3].T*self._weights[-1])
#derivError_out = delta_out * layers[-3].T*self._weights[-1]
derivError_out = delta_out * layers[-3].T
变化

  • 当初始输出为0.5或更大时,网络可以学习0和1之间的任何值Yay
  • 当初始输出小于0.5时,网络可以学习任何小于“不大于起始输出”的值
    • 这种行为取决于权重,并且似乎存在基于网络无法学习的权重的上限。当尝试学习大于该限制的值时,它将收敛到0

一个立即出现的问题是,您的乙状结肠衍生物似乎不正确。sigmoid(x)的导数不等于(x)*(1-x),而是等于sigmoid(x)(1-sigmoid(x))

您可以相应地更改实现

def logistic(x, deriv=False):
    """
    If using the derivative, input must be result of logistic
    """
    phi = 1 / (1 + np.exp(-x))

    if deriv:
        return phi * (1 - phi)

    return phi
我发现有两件事我非常怀疑你在做什么:

  • 您正在优化的错误不是理想的成本函数
    梯度下降可以最大限度地减少您正在制定的
    错误

    查看当前公式
    error=prediction-target
    一个正常运行的梯度优化器所能达到的最佳结果就是给出尽可能最小的(如果激活允许它甚至是负的)预测

    建议:使用一些L-范数作为误差函数,例如L1范数
    误差=|预测-目标|
  • 也许我只是看不清楚,没关系,但是权重更新
    derivError\u out=delta\u out*层[-3].t*self.\u权重[-1]

    看起来可疑,一旦构建了错误项
    delta_out
    ,您想要将贡献反向传播到各个权重,这仅仅取决于它们各自的激活
    层[-3]
  • 我建议如下:(如果你感兴趣的话)

    此外:您的学习率
    eta
    似乎相当高;这肯定会产生分歧,并产生一个权重爆炸的模型,最终根据初始化生成0或1

    降低音量以实现更稳定的梯度下降

    例如


    增加神经网络参数的正确推导可以在互联网上找到。在感知机的情况下,每个偏差都会增加一个由其所连接的输出节点确定的“增量”值。我的偏差节点不是简单地实现这一点,而是通过自身和这个“增量”的乘积递增

    在我的无能中,我把这个表达写成

    delta_out = error * activation(layers[-1], deriv = True)
    derivError_bias = delta_out * self._bias[-1].T
    self._bias -= eta * derivError_bias
    
    而不是
    self.\u bias-=eta*delta\u out


    网络现在可以学习任何随机分配的目标,以及任何随机分配的权重和偏差。

    不过,我想这并不能解决你所有的问题,现在我看到了。一旦我算出导数,我就会回来,重新计算导数的sigmoid是浪费时间的,之前已经计算过了。然而,这就是我开始这项工作时的实现。标准实现基本上要求您输入您想要的效率派生结果。我没有提到它,但是改变eta只会影响我测试中的收敛速度。网络继续在相同的价值观上趋同。y我目前正在编辑我的答案,以解决实际问题,记录周期时间;按照目前的方式,您可以为每个调用计算
    phi=1/(1+np.exp(-something))
    ,但是它仍然令人困惑,而且一旦您开始实现不同的非线性,而不仅仅是函数值的组合,它可能会出大问题^;天啊,我好像也有一段时间了。我在纸上的推导中也犯了同样的错误
    def learn(self, input, activation, epoch=10, eta=1, debug=False):
        """ .... optimizing L1-norm .... """
        for e in range(epoch):
            layers = self._forwardProp(input, activation)
    
            diff = layers[-1] - self._target
            error = np.abs(diff)
    
            delta_out = np.sign(diff) * activation(layers[-2], deriv=True)
            derivError_out = delta_out * layers[-3].T
            self._weights -= eta * derivError_out
            self._bias -= eta * delta_out
    
    nn1.learn(x, logistic, 100, eta=1e-2, debug=True)
    
    delta_out = error * activation(layers[-1], deriv = True)
    derivError_bias = delta_out * self._bias[-1].T
    self._bias -= eta * derivError_bias