【问题标题】:Feed-Forward Neural Network Linear Function前馈神经网络线性函数
【发布时间】:2015-08-20 02:30:18
【问题描述】:

我是 ANN 的新手。在线阅读文章后,我用 C++ 实现了一个 FF 神经网络。在构造函数的参数中,这些是重要的参数:

  1. 隐藏层数
  2. 每层的节点数(输入、隐藏、输出)
  3. 每个隐藏层和输出层的激活函数

我想要输出两个十进制数字,通常范围在 -xx.xx 到 +xx.xx 之间,但有时可以达到数百个。

我想我会使用一个隐藏层并使用 sigmoid 函数。输出层将是一个线性函数,因此该值不会被限制为 [-1, 1]。

我在网上搜索了许多关于激活函数的初学者资源,但大多数只提到了对数 Sigmoid / 双曲正切和其他非线性函数。我只是对线性函数的用法完全感到困惑。

我的问题是:

  1. 我应该只使用 f(x) = x 作为我的线性函数吗?这个常用吗?或者我应该更多地尝试使用 f(x) = ax + b 等函数的系数吗?

  2. 当我进行反向传播时,到目前为止我所阅读的所有内容都提到了使用激活函数的导数来计算增量。如果使用线性函数,它是如何工作的?如果我使用 f(x) = x,则导数应该为 1。因此,我的实现使用 1 来计算误差并调整权重,就像使用非线性激活函数一样。我完全走错了方向吗?我完全糊涂了,因为我读到的所有资源都没有提到这一点。

谢谢!

【问题讨论】:

    标签: c++ neural-network backpropagation


    【解决方案1】:

    我认为在这里区分用于隐藏层的激活函数和用于输出层的激活函数很有用。在许多模型中,这些激活是不一样的,虽然反向传播算法并不关心这一点,但我认为它在概念上非常重要。

    典型的神经网络架构由一个输入“层”、一个或多个隐藏层和一个输出层组成。 (我将输入层放在引号中,因为该层通常没有任何关联的参数;它只是将输入合并到模型中的一种方式。)给定一个输入向量x,信息通过模型向前流动,激活每个依次隐藏层,最后激活输出层。

    让我们考虑一个具有一个输入“层”、一个隐藏层和一个输出层的网络。该模型中的信息流为:

    x  -->  h(x) = s(Wx + b)  -->  y(x) = r(Vh(x) + c)
    

    在这里,我将隐藏层的输出表示为h(x),将输出层的输出表示为y(x)。每一层都构造为其输入的加权和,并结合一些偏移或偏差(这是隐藏层的affine transformationWx + b,输出层的Vh + c。此外,每一层的仿射输入变换由一个可能的非线性“激活函数”进一步转换:s(.) 用于隐藏层,r(.) 用于输出层。

    示例:分类器

    假设这个网络被用于二进制分类。如今,sr 都使用 logistic function 非常普遍:s(z) = r(z) = (1 + e^-z)^-1,但出于不同的原因使用这种方式:

    • 对于隐藏层,使用逻辑函数会导致模型的内部表示h(x) 成为x 的非线性函数。与使用线性激活 s(z) = z 相比,这使模型更具代表性。

    • 对于输出层,logistic函数保证模型的输出y(x)可以看成是一个伯努利随机变量的概率。

    示例:回归

    现在让我们假设您正在使用这样的网络进行回归。回归问题需要在开区间 (0, 1) 之外对输出进行建模是很常见的。在这些情况下,使用逻辑函数作为隐藏层s(z) = (1 + e^-z)^-1 的激活是非常常见的,但输出层是线性激活r(z) = z,所以y(x) = Vh(x) + c。使用这些激活函数的原因是:

    • 对于隐藏层,使用非线性激活可以使模型更具代表性——就像上面的分类器模型一样。

    • 对于输出层,线性激活确保模型可以实现任意范围的输出值。本质上,模型的输出是对隐藏层所表示的任何内容的基本仿射变换(缩放、旋转、倾斜和/或平移)。

    基本上,这是一种有点冗长的说法,听起来您描述的方法对您的问题有好处 - 对隐藏层使用非线性激活,对输出使用线性激活。

    反向传播

    反向传播是最广泛使用的优化神经网络参数的方法。基本上反向传播是梯度下降;要使用它,我们需要根据模型中的参数(WbVc)制定损失函数。

    对于回归,通常使用的损失是均方误差 (MSE):

    L(W, b, V, c) = 1/n * sum i = 1..n (y(X[i]) - t[i])^2
    

    在这里,我假设我们可以访问由n 输入X[i] 和相应的目标值t[i] 组成的训练数据集。网络输出 y 是作为其输入 X[i] 的函数计算的,并将结果与​​ t[i] 进行比较——任何差异都会被平方并累积到整体损失中。

    为了优化模型的参数,我们需要对损失求导,并将其设置为零。因此,对这种损失进行导数得到如下结果:

    dL/dW = 1/n sum i = 1..n 2(y(X[i]) - t[i]) y'(X[i])
    

    在这里,我们使用了链式法则来扩展损失的导数,以包括网络输出的导数。这个扩展过程在模型中一直“向后”继续,直到无法进一步应用链式规则扩展。

    但是,您可以从这里开始看到输出函数的导数的应用。对于回归模型,y(x) = Vh(x) + c,所以y'(x) = Vh'(x)。所以:

    dL/dW = 1/n sum i = 1..n 2(y(X[i]) - t[i]) Vh'(X[i])
    

    但是h(x) = s(Wx + b) 所以h'(x) = xs'(Wx + b)(请记住,这里我们取的是关于W 的导数)。

    无论如何,获取所有导数变得相当复杂,正如您可以看到的仅针对两层网络(或具有一个隐藏层的网络),但激活函数的导数只是应用的自然结果区分模型的整体损失时的链式法则。

    【讨论】:

    • 所以扔掉所有的数学术语,从编码的角度来看,我不关心激活函数是线性的还是非线性的——我仍然会调用任何函数(这里我的意思是一个 C++ 函数)我用来计算导数(如果 f(x) = x,f'(x) 将是 1)来执行反向传播算法,并且不需要进行额外的检查,例如:如果(非线性)然后找到导数,否则{为线性函数做一些事情}等。
    • 是的,没错——链式法则只适用于计算损失的导数,不管它是什么。
    【解决方案2】:

    f(x) = x 是一个非常基本的激活函数并且不常用,因为它不能用于对在大多数数据问题中更常见的复杂模式进行建模。常用的激活函数有 sigmoid、双曲正切、loglogistic、RBF(高斯)和 Elliot 等。对我来说,双曲正切在很多场合都很好用,我总是从那个开始我的初始模型设置。函数 f(x) = ax + b 与具有偏差和权重的线性函数相同。如果您使用任何随机函数进行激活,您可能不会得到合理的结果。

    反向传播在训练期间完成(即更新神经网络的权重),如果 f(x)=x,它只会更新偏置神经元。还有几种类型的反向传播算法,更常用的是“标准反向传播”。您可以在 Google 上轻松找到该算法。

    【讨论】:

      【解决方案3】:

      定义一个可逆函数f(x),使得-1f_inv(y)。任何选择都行得通,因为此功能是在已训练的网络部分之外应用的。

      这也意味着您不计算权重与原始训练值的导数,而只计算缩放后的训练值。对于线性映射,这无关紧要(如df/dx=a),但它可能会导致非线性映射的结果略有不同。总的来说,这是好是坏,很难说。

      【讨论】: