65.9K
CodeProject 正在变化。 阅读更多。
Home

AI:初学者神经网络(第 2 部分,共 3 部分)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.87/5 (126投票s)

2006年11月24日

CPOL

8分钟阅读

viewsIcon

365308

downloadIcon

9507

人工智能:神经网络入门(多层网络/反向传播)

引言

本文是我将要发布的3篇文章系列的第2部分。拟定的文章内容如下:

  1. 第1部分:介绍感知机网络(单层神经网络)。
  2. 第2部分:本文介绍多层神经网络以及反向传播训练方法,用于解决非线性分类问题,例如异或(XOR)逻辑门。这是感知机无法做到的。本文将进一步解释这一点。
  3. 第3部分:将介绍如何使用遗传算法(GA)来训练多层神经网络以解决某些逻辑问题。

摘要

本文将展示如何使用多层神经网络来解决异或(XOR)逻辑问题。

简要回顾(来自第1部分)

在开始讨论本文主要内容——多层神经网络之前,让我们先回顾一些关键概念。如果您还没有阅读第1部分,可能需要从那里开始。

感知机配置(单层网络)

下图所示的输入(x1,x2,x3..xm)和连接权重(w1,w2,w3..wm)通常是实数值,包括正数(+)和负数(-)。

感知机本身由权重、求和处理器、激活函数和一个可调阈值处理器(以下称为偏置)组成。

为了方便起见,通常的做法是将偏置视为另一个输入。下图说明了修改后的配置。

偏置可以被认为是感知机无论其输入如何都倾向于激活的倾向(一种倾向于某种行为方式)。如上图所示的感知机配置网络,当加权和大于0时会激活,或者如果您在数学类型的解释中

这就是感知机的基本操作。但现在我们想构建更多这样的层,所以让我们继续学习新内容。

现在是新内容(更多层)

从现在开始,所有讨论都直接与本文的代码相关。

在顶部的摘要中,我们要解决的问题是如何使用多层神经网络来解决异或(XOR)逻辑问题。那么这是如何实现的呢?这实际上是对第1部分已讨论内容的逐步构建。所以让我们继续前进。

异或(XOR)逻辑问题是什么样的?好吧,它看起来像下面的真值表。

请记住,使用单层(感知机)我们实际上无法实现异或(XOR)功能,因为它不是线性可分的。但使用多层网络,这是可以实现的。

新网络是什么样的

将用于解决异或(XOR)问题的新网络将与单层网络相似。我们仍然处理输入/权重/输出。新的是隐藏层的添加。

如上所述,有一个输入层,一个隐藏层和一个输出层。

通过使用输入和权重,我们可以计算给定节点的激活。这对于隐藏层来说很容易实现,因为它直接连接到实际的输入层。

然而,输出层对输入层一无所知,因为它没有直接连接到输入层。因此,要计算输出节点激活,我们需要利用隐藏层节点的输出来作为输出层节点的输入。

上述整个过程可以被认为是从一层到下一层的正向传播。

这仍然像单层网络一样工作;任何给定节点的激活仍然按如下方式计算:

其中(wi是权重(i),Ii是输入(i)值)

你看,这和以前一样,没有什么神秘之处,也没有什么魔法。这是我们已经涵盖过的内容。

所以这就是网络的构成/工作方式。现在我想您一定想知道如何训练它。

学习类型

神经网络本质上可以应用两种学习类型:“强化学习”和“监督学习”。

强化学习

在强化学习中,训练期间,将一组输入呈现给神经网络,输出为0.75,而目标期望值为1.0。

误差(1.0 - 0.75)用于训练(“错误0.25”)。

如果有2个输出,则将总误差求和得到一个数字(通常是均方误差之和)。例如,“所有输出的总误差为1.76”。

请注意,这只告诉您错了多少,而没有告诉您错的方向。

使用这种方法,我们可能永远无法得到结果,或者可能出现“大海捞针”的情况。

注意:本系列的第3部分将使用遗传算法(GA)来训练神经网络,这是强化学习。遗传算法(GA)只是做它该做的事情,并进行所有正常的遗传算法阶段来选择神经网络的权重。没有反向传播值。神经网络只是好或坏。可以想象,这个过程需要更多步骤才能获得相同的结果。

监督式

在监督学习中,神经网络会获得更多信息。
不仅仅是“错了多少”,而是“错了哪个方向”,就像“大海捞针”,但您被告知“向北一点”,“向西一点”。

因此,在监督学习中,您获得并使用更多信息,这是神经网络学习算法的正常形式。反向传播(本文使用的,是监督学习)。

学习算法

简而言之,训练多层神经网络需要执行以下步骤:

  • 从神经网络中的随机权重(和偏置)开始。
  • 尝试训练集中的一个或多个成员,查看输出(们)与应有的值(与目标输出(们))相比有多差。
  • 稍微调整权重,目标是改进输出。
  • 现在尝试使用新的训练集,或重复再次,
    每次都调整权重。
  • 重复此过程,直到获得相当准确的输出。

这就是本文提交的内容用来解决异或(XOR)问题。这也被称为“反向传播”(通常称为BP或BackProp)。

反向传播允许您使用输出处的误差来调整到达输出层的权重,然后还可以计算向前1层的有效误差,并使用此误差来调整到达该层的权重,依此类推,将误差反向传播通过任意数量的层。

诀窍在于使用Sigmoid作为非线性传递函数(这在第1部分中已涵盖)。Sigmoid的使用是因为它提供了应用微分技术的可能性。

因为它是很好微分的,所以正好有

在本文的上下文中可以写成

delta_outputs[i] = outputs[i] * (1.0 - outputs[i]) * (targets[i] - outputs[i])

正是通过使用这种计算,权重更改才能反向应用于网络。

需要注意的事项

山谷:使用滚球的比喻,很可能会出现像这样的山谷,两侧陡峭,底部平缓。梯度下降倾向于浪费时间在山谷的两侧来回滑动(想象一下球!)。

那么我们该如何解决这个问题呢?我们可以添加一个动量项,它倾向于抵消来回的移动,并强调任何一致的方向,然后它将更成功(更快)地穿过底部斜率很缓的山谷。

开始训练

这最好通过本文实际代码中的一个代码片段来演示。

/// <summary>
/// The main training. The expected target values are passed in to this
/// method as parameters, and the <see cref="NeuralNetwork">NeuralNetwork</see>
/// is then updated with small weight changes, for this training iteration
/// This method also applied momentum, to ensure that the NeuralNetwork is
/// nurtured into proceeding in the correct direction. We are trying to avoid valleys.
/// If you don't know what valleys means, read the articles associated text
/// </summary>
/// <param name="target">A double[] array containing the target value(s)</param>
private void train_network(double[] target)
{
    //get momentum values (delta values from last pass)
    double[] delta_hidden = new double[nn.NumberOfHidden + 1];
    double[] delta_outputs = new double[nn.NumberOfOutputs];

    // Get the delta value for the output layer
    for (int i = 0; i < nn.NumberOfOutputs; i++)
    {
        delta_outputs[i] =
        nn.Outputs[i] * (1.0 - nn.Outputs[i]) * (target[i] - nn.Outputs[i]);
    }
    // Get the delta value for the hidden layer
    for (int i = 0; i < nn.NumberOfHidden + 1; i++)
    {
        double error = 0.0;
        for (int j = 0; j < nn.NumberOfOutputs; j++)
        {
            error += nn.HiddenToOutputWeights[i, j] * delta_outputs[j];
        }
        delta_hidden[i] = nn.Hidden[i] * (1.0 - nn.Hidden[i]) * error;
    }
    // Now update the weights between hidden & output layer
    for (int i = 0; i < nn.NumberOfOutputs; i++)
    {
        for (int j = 0; j < nn.NumberOfHidden + 1; j++)
        {
            //use momentum (delta values from last pass),
            //to ensure moved in correct direction
            nn.HiddenToOutputWeights[j, i] += nn.LearningRate * delta_outputs[i] * nn.Hidden[j];
        }
    }
    // Now update the weights between input & hidden layer
    for (int i = 0; i < nn.NumberOfHidden; i++)
    {
        for (int j = 0; j < nn.NumberOfInputs + 1; j++)
        {
            //use momentum (delta values from last pass),
            //to ensure moved in correct direction
            nn.InputToHiddenWeights[j, i] += nn.LearningRate * delta_hidden[i] * nn.Inputs[j];
        }
    }
}

最后是代码

本文的代码看起来像下面的类图(它是Visual Studio 2005 C#,.NET v2.0)。

人们应该花时间查看的主要类是:

  • NN_Trainer_XOR :训练一个神经网络来解决异或(XOR)问题。
  • TrainerEventArgs :训练事件参数,用于GUI。
  • NeuralNetwork :一个可配置的神经网络。
  • NeuralNetworkEventArgs:训练事件参数,用于GUI。
  • SigmoidActivationFunction :一个静态方法,提供Sigmoid激活函数。

其余的是我构建的一个GUI,仅用于展示所有内容如何协同工作。

注意:演示项目包含所有代码,因此我不会在此列出。

代码演示

附带的DEMO应用程序有3个主要区域,如下所述:

实时结果标签

可以看出,它已经近乎解决了异或(XOR)问题(您可能永远无法获得100%准确)。

训练结果标签

查看训练阶段的目标/输出。

查看训练阶段的误差。

已训练结果标签

查看已训练的目标/输出。

查看已训练的误差。

还可以使用“查看神经网络配置”按钮查看神经网络的最终配置。如果人们对神经网络最终获得的权重感兴趣,可以在这里查看。

您怎么看?

就是这样了。我只想请求一下,如果您喜欢这篇文章,请为它投票。

关注点

我认为AI非常有趣,这就是为什么我花时间发表这些文章。所以我希望其他人也觉得它有趣,并且它能帮助人们增长知识,就像它帮助了我一样。

任何想进一步了解AI相关内容,并觉得本文内容有些基础的人,都应该看看Andrew Krillov的文章,在Andrew Krillov CP的文章,他的文章更高级,而且非常好。事实上,Andrew所做的一切都非常好。

历史

  • v1.0 2006/11/24

参考文献

  • 人工智能第二版,Elaine Rich / Kevin Knight。McGraw Hill Inc。
  • 人工智能,一种现代方法,Stuart Russell / Peter Norvig。Prentice Hall。
© . All rights reserved.