带你读《C#神经网络编程》之一:快速预览

智能系统与技术丛书
点击查看第二章
点击查看第三章
C#神经网络编程
Hands-On Neural Network Programming with C#

带你读《C#神经网络编程》之一:快速预览

[美]马特·R.科尔(Matt R.Cole) 著
刘 安 译

第1章

快速预览
欢迎阅读本书。感谢你购买本书并与我们一起学习。现在,无论你走到哪里,听到和读到的都是机器学习、人工智能、深度学习、神经元、人工智能等术语。但是,比较尴尬的是,与你交谈的每个人对这些术语的理解好像都略有不同。
在本章中,我们将讨论一些非常基本的神经网络术语,为后续章节奠定基础。我们需要统一术语用词,以确保在后面章节中的相关表述都规范明确。
本书的目标是让C#开发人员尽快开始行动起来。为此,我们将尽可能使用开源库,但也必须开发一些自定义的应用程序,我们将提供这些应用程序的源代码。总之,我们希望你能够以最快的速度和最小的努力将这些功能应用到你自己的程序中。
好的,让我们开始吧。
神经网络已存在很多年,但在过去几年才开始兴起,现在已成为一个热门话题,当然,这也是我们写这本书的原因。本书的目标是帮助你拨云见日,帮助你通过神经网络走向成功。本书只面向C#.NET开发人员,他们能够方便地获取资源,并应用到项目中,书中不提供我们更常见的Python、R和MATLAB代码资源。如果你安装了Visual Studio,并且有强烈的学习愿望,那么让我们开始探索之旅吧。
首先,我们要明确一些事情,在写本书时,假设读者基本未接触过神经网络,如果你对神经网络有一些了解,那么可以随意跳到你感兴趣的章节阅读。还假设你是一位经验丰富的C#开发人员,并使用C#、.NET和Visual Studio编写过应用程序,而无论你使用的是哪些版本进行开发。再次重申,我们的目标不是C#语法、.NET框架或Visual Studio本身,而是向开发人员提供尽可能多的宝贵资源,这样他们就可以修改代码并开发出最好的应用程序。
现在,你已了解了本书的目的,肯定期待直接进入主题并开始编码,但为了提高效率,首先我们必须花一些时间来重温一些基础知识—一些理论,一些有趣的概念,一些晦涩难懂的内容。别担心,我们不会深入讲解理论,很快就将带你编写并解读源代码。
此外,请记住,该领域的研究正在快速发展之中,今天最新、最好的下个月可能就被超越了。因此,请仅把本书当作不同的研究和意见的概述,本书不是神经网络所有东西和全部内容的圣经,也不应该这样认为。你很可能会遇到与作者有不同意见的人,也会碰到以不同方式写应用和函数的人。其实这样很好—尽可能多地收集信息,并做出明智的选择,只有这样才能奠定更加坚实的知识基础。
在本章中,我们将讨论以下主题:

  • 神经网络概述
  • 神经网络在当今企业中的作用
  • 学习类型
  • 了解感知器
  • 了解激活函数
  • 了解后向传播

技术要求
C#基础知识是理解本书中应用程序的必要条件。此外,Microsoft Visual Studio(任何版本)是开发应用程序的首选软件。

1.1 神经网络概述

首先我们来准确定义神经网络。请注意,你可能还听过人工神经网络(ANN)的说法。
虽然我不喜欢人工一词,但在本书中这些术语可以互换使用。
“最简单的说法是,神经网络是一个包含几个简单但高度互连的元素的系统,每个元素都根据其对外部输入的响应来处理信息。”
你是否知道神经网络是模仿哺乳动物大脑的大脑皮层,并建模得来的?为什么我不说它们是根据人类大脑皮层建模的呢?因为生物学和计算机研究的很多例子来自老鼠、猴子,当然还有人类的大脑。大型神经网络可能有数百甚至数千个处理单元,其中哺乳动物的大脑有数十亿个。是神经元创造了神奇,实际上我们可以单独写一本关于该主题
的书。
这就是为什么我说是它们让所有的奇迹发生:如果我给你看了哈莉·贝瑞的照片,你就会马上认出她。你没有时间分析事物,你的认知基于一生收集的知识,同样,如果我对你说了比萨这个词,你就会有一个直接的心理影像,甚至可能开始变得饥饿。这一切是如何发生的?神经元!即使今天的神经网络继续快速发展,但与有史以来的终极神经网络—人类大脑相比,它们显得苍白无力。关于这个神经网络还有很多我们不了解,等一下,让我们来看看神经网络会变成什么样。
神经网络被组织成由所谓的节点或神经元组成的层。这些节点本身就是神经元,并且相互连接(在本书中,术语节点和神经元可互换),信息被传递到输入层,由一个或多个隐藏层处理,然后被提供给输出层以进行最终(或继续进行)处理—计算误差、反馈调整,重复进行!
你可能会问,什么是神经元?
“神经元是神经网络中计算的基本单位。”
正如前面提到的,神经元有时也被称为节点或单元。它接收来自其他节点或外部源的输入并计算输出。每个输入都有一个相关的权重(见图1-1中的w1和w2),它是根据输入信息的相对重要性进行分配的。节点将输入的加权和提供给函数f(激活函数,后面将详细介绍)。虽然这是对神经元是什么以及它能做什么的极端简化,但基本上就是这样。
让我们直观地了解一下从单个神经元到非常深的学习网络的过程。图1-1展示了单个神经元的直观情形。

带你读《C#神经网络编程》之一:快速预览

图1-2显示了一个由几个神经元组成的非常简单的神经网络。

带你读《C#神经网络编程》之一:快速预览

这是一个更复杂或更深的网络,如图1-3所示。

带你读《C#神经网络编程》之一:快速预览

1.1.1 神经网络训练

现在我们已经知道神经网络和神经元是什么了,我们来谈谈它们能做什么以及如何做的。神经网络如何学习?那些有孩子的人已经知道了这个问题的答案,如果你希望你的孩子知道猫是什么,你会怎么做?你会向他展示猫(图片或真实的猫)。你希望你的孩子知道狗是什么,应该给他看狗。神经网络在概念上没有什么不同,它也是一种学习规则形式,它将修改输入层的输入权重,通过隐藏层处理它们,再通过激活函数对它们进行最终处理,在我们的例子中,它最终能够识别猫和狗。并且,如果以上处理过程正确完成,猫不会被识别成狗!
神经网络最常见的学习规则之一就是所谓的delta规则。这是一种监督规则,每次向网络呈现另一种学习模式时都会调用该规则,每次发生这种情况都称为循环或轮次,每当输入模式通过一个或多个前向传播层,然后通过一个或多个后向传播层,就会发生规则的调用。
更简单地说,当将一张图片输入神经网络时,它试图确定图片上可能是什么,正确答案与我们的猜测之间的差异是误差或误差率。我们的目标是使误差率最小化或最大化。在最小化的情况下,我们需要每次猜测的误差率尽可能接近0,越接近0,我们就越接近成功。
随着不断进展,进行所谓的梯度下降,意味着我们继续朝着全局最小,即最低的误差方向前进,这对于成功至关重要。
一旦网络经过训练,并且你感到满意,训练周期就可以结束了,你可以进入测试阶段。在测试周期内,仅使用前向传播,模型在该过程的输出结果将用于进一步分析,同样,在测试期间不进行后向传播。

1.1.2 神经网络的结构指南

在本节中,我们可以写一大段文字来描述神经网络的所有组合以及它们的形态。但是,一幅图胜过千言万语,如图1-4所示(见彩插)。

带你读《C#神经网络编程》之一:快速预览

我们来谈谈图1-4中一些比较常见的网络:

  • 感知机网络:这是最简单的前馈神经网络,如图1-5(见彩插)所示,它不包含任何隐藏层。
  • 前馈网络:该网络可能是最简单的人工神经网络设计类型。它包含以层排列的多个神经元(节点),来自相邻层的节点之间具有连接或边,每个连接都有与它们相关的权重,如图1-6(见彩插)所示。
  • 循环神经网络(RNN):RNN被称为循环,因为它对序列中的每个元素执行相同的任务,输出取决于先前的计算。它还能回顾前面的步骤,形成一种短期记忆,如图1-7(见彩插)所示。

带你读《C#神经网络编程》之一:快速预览

1.2 神经网络在当今企业中的作用

作为开发人员,我们主要关注的是如何将我们正在学习的内容应用到现实场景中。更具体地说,在企业环境中,有哪些使用神经网络的机会?以下是一些神经网络的应用场景(还有很多这样的场景):

  • 在不理解变量之间的关系的情况下。
  • 在难以描述关系的场景中。
  • 在一个目标是发现数据中的不规则模式的场景中。
  • 分类数据以识别模式,如识别动物、车辆等。
  • 信号处理。
  • 图像识别(情绪、情感、年龄、性别等)。
  • 文本翻译。
  • 手写识别。
  • 自动驾驶。
  • 更多其他场景。

1.3 学习的类型

前面我们谈到了神经网络学习,下面简要介绍三种不同类型的学习:有监督学习、无监督学习和强化学习。

1.3.1 有监督学习

如果你有一个与已知结果匹配的大型测试数据集,那么有监督学习可能是一个不错的选择。神经网络将处理数据集,将神经网络输出并与已知结果进行比较,调整网络参数,然后重复。很简单,对吧?

1.3.2 无监督学习

如果你没有任何测试数据,并且可以以某种方式从数据行为中获得损失函数,那么无监督学习对你来说可能是一个不错的选择。神经网络将处理数据集,使用cost函数来判断误差率是多少,调整神经网络参数,然后重复。这一切都是实时进行的!

1.3.3 强化学习

最后一个学习类型是强化学习,在某些圈子中被称为胡萝卜加大棒。神经网络对数据集进行处理,从数据中学习。如果误差率降低,我们就会得到胡萝卜(奖励);如果误差率增加,我们就会得到大棒(惩罚)。够清楚了,对吧?

1.4 了解感知器

我们要处理的最基本的元素称为神经元,如果我们对神经元使用最基本的激活函数,那么这个函数只有两种可能结果:1和0。直观上,这样的函数如图1-8所示。

带你读《C#神经网络编程》之一:快速预览

如果输入为正或0,则该函数返回1,否则返回0。具有这种激活函数的神经元称为感知器,它是我们可以开发的最简单的神经网络形式。直观上,它如图1-9所示。
感知器遵循前馈模型,意味着输入被传送到神经元,并被处理,然后产生输出。输入进来,输出结束。我们来举个例子。
假设有一个具有两个输入的感知器,如图1-9所示。为了更好地演示,假设输入1为x1,输入2为x2,为这两个变量赋值,如下所示:
输入1:x1 = 12
输入2:x2 = 4

带你读《C#神经网络编程》之一:快速预览

每个输入都必须加权,即乘以某个值,该值通常介于-1到1之间。当我们创建感知器时,首先为它们分配随机权重,例如,对于输入1(x1),我们将其权重记为w1,对于输入2(x2),将其权重记为w2。这个感知器的权重如下:处理过程如图1-10所示。
权重1:0.5
权重2:-1

带你读《C#神经网络编程》之一:快速预览

一旦输入被加权,现在需要对它们求和。使用前面的示例,将有:
6 + (-4) = 2
然后,该总和被传递给激活函数进行处理,激活函数将在后面的章节中详细介绍。这将产生感知器的输出,激活函数最终会告诉感知器它是否可以“启动”,即激活。
现在,我们将使用一个非常简单的激活函数,如果和为正,则输出为1,如果和为负,则输出为-1。
因此,在伪代码中,单个感知器的算法如下:

  • 对于每个输入,将该输入乘以其权重;
  • 对所有加权的输入求和;
  • 根据通过激活函数的总和(总和的符号如为正,则输出1,如为负,则输出-1)来计算感知器的输出。

这有用吗
是的,事实上这很有用,让我们来详细阐述。将输入向量视为点的坐标,对于具有n个元素的向量,该点将在n维空间中。拿一张纸,在这张纸上绘制一组点,然后用一条直线将这些点分成两组,现在你的纸应该如图1-11所示。

带你读《C#神经网络编程》之一:快速预览

如你所见,现在这些点分为两组,线的两边各一组。如果能用单条直线清楚地分离所有点,那么这两组点就是所谓的线性可分。
无论你信不信,单一感知器能够学习拟合这条线,当你的程序完成时,感知器还能判断一个点在线上方还是下方(或在线左侧还是右侧,这取决于线如何绘制)。
让我们快速编写一个Perceptron类,这样对于那些喜欢阅读代码而非文字的人来说就更清楚了(就像我一样!)。我们的目标是创建一个简单的感知器,它可以确定一个点应该在线的哪一侧,就像图1-11一样:
带你读《C#神经网络编程》之一:快速预览

构造函数接收一个参数以指示输入的数量(在这种情况下为3个:x、y和偏差),并相应地调整数组的大小:
带你读《C#神经网络编程》之一:快速预览

随机选择权重:
带你读《C#神经网络编程》之一:快速预览

接下来,需要感知器的一个函数来接收其信息,该信息的长度与权重数组的长度相同,然后将输出值返回。我们将其称为feedforward:
带你读《C#神经网络编程》之一:快速预览

结果是总和的正负情况,它将是-1或+1。在这种情况下,感知器将猜测输出应该在线的哪一侧:
带你读《C#神经网络编程》之一:快速预览

到目前为止,我们有了一个功能最小的感知器,它应该能对点所在的位置做出有根据的猜测。
创建Perceptron:
带你读《C#神经网络编程》之一:快速预览

输入是3个值:x、y和偏差:
带你读《C#神经网络编程》之一:快速预览

得到答案:
带你读《C#神经网络编程》之一:快速预览

要让感知器更有价值就需要训练它,这样它才能做出有根据的猜测。我们通过创建如下的train函数来做到这一点:

  1. 引入一个新变量来控制学习率:
    带你读《C#神经网络编程》之一:快速预览

2.提供输入和已知答案:
带你读《C#神经网络编程》之一:快速预览

3.根据提供的输入进行有根据的猜测:
带你读《C#神经网络编程》之一:快速预览

4.计算误差,即答案和我们的猜测之间的差异:
带你读《C#神经网络编程》之一:快速预览

5.最后,根据误差和学习常量调整所有权重:
带你读《C#神经网络编程》之一:快速预览

所以,既然你已经了解了感知器,让我们将激活函数添加到感知器中并将其提升到新的水平!

1.5 了解激活函数

激活函数被添加到神经网络的输出端以确定输出。它通常会将结果值映射到-1到1的范围内,具体取决于选择的激活函数。它最终用于确定神经元是否会“启动”或“激活”,就像灯泡开启或关闭一样。
激活函数是输出之前神经网络的最后一部分,可以将其视为输出值的提供者。有多种激活函数可供使用,图1-12仅显示了其中的一小部分。

带你读《C#神经网络编程》之一:快速预览

有两种类型的激活函数—线性和非线性:

  • 线性:线性函数是在直线上或几乎在直线上的函数,如图1-13所示。


带你读《C#神经网络编程》之一:快速预览

  • 非线性:非线性函数是不在直线上的函数,如图1-14所示。

带你读《C#神经网络编程》之一:快速预览

1.5.1 激活函数绘图

在使用激活函数之前,从直观上理解激活函数的形态非常重要。我们将绘制几个激活函数,然后进行基准测试,以便你查看。
图1-15是单独绘制逻辑陡峭近似和Swish激活函数时的样子。图1-16显示了所有激活函数绘制在一起时的样子。

带你读《C#神经网络编程》之一:快速预览

带你读《C#神经网络编程》之一:快速预览

注意:可以从GitHub(https://github.com/colgreen/ sharpneat) 上的SharpNeat项目下载并运行以上程序。
此时,你可能想知道我们为什么关心这些激活函数的图形形状—问得非常好。当你深入了解神经网络并动手实践时,将会非常频繁地应用这些知识。知道你的激活函数是否会将神经元的值置于开启或关闭状态,以及激活函数需要的输入范围,对你来说将非常方便。作为一名机器学习开发者,毫无疑问,在你的职业生涯中将会使用激活函数来处理异或问题,了解Tanh和LeakyRelu激活函数之间的区别非常重要。

1.5.2 函数绘图

在该示例中,我们将使用开源软件包SharpNeat。该软件包是在各种场景下都非常强大的机器学习平台之一,它包含一个特殊的激活函数绘图仪。你可以从https://github.com/colgreen/sharpneat下载最新版本。在此例中,我们将构建包含ActivationFunctionViewer的项目,如图1-17所示。

带你读《C#神经网络编程》之一:快速预览

打开该项目后,搜索PlotAllFunctions函数。这个函数负责所有激活函数的绘图。我们详细介绍一下这个函数:
带你读《C#神经网络编程》之一:快速预览
带你读《C#神经网络编程》之一:快速预览
带你读《C#神经网络编程》之一:快速预览

关键代码我们用阴影部分突出显示,这是传入的激活函数执行的地方,它的值用于y轴绘图。著名的ZedGraph开源绘图包用于所有的图形绘图。一旦函数被执行,就将绘制相应的图。

1.6 了解后向传播

后向传播误差向后传播之意,是一种使用梯度下降的有监督学习神经网络算法。算法中计算所谓的梯度误差函数,其对应于网络权重,它是感知器delta规则的一般形式,并一直拓展到多层前馈神经网络。
与前向传播不同,后向传播通过在网络中后向移动来计算梯度。首先计算最后一层权重的梯度,依次计算,最后计算第一层的梯度。随着最近深度学习在图像和语音识别领域的普及,后向传播再次成为人们关注的焦点。就所有意图和目的而言,它是一种有效的算法,而当今版本利用GPU来进一步提高计算性能。
最后,由于后向传播计算取决于前向阶段的激活和输出(所有层的非误差项,包括隐藏层),所有这些值都必须在后向传播开始之前进行计算,因此,对于梯度下降的每次迭代来说,前向计算必须在后向传播之前完成。
前向和后向传播差异
现在,我们来说明前向和后向传播之间的区别,一旦理解了这一点,你就可以更好地理解和可视化整个神经网络的运行方式。
在神经网络中,向前传播数据以获取输出,然后将其与实际预期值进行比较以获得误差,这是正确数据与机器学习算法预测数据之间的差异。为了最小化该误差,现在你必须求每个权重的误差导数来向后传播,然后从权重中减去该误差导数值。
训练神经元启动、激活或抑制的时间,就是神经网络进行学习的时候,每个神经元应仅针对某类输入激活,而非全部。因此,通过向前传播,可以看到神经网络的行为表现并找到误差。在找出网络误差率后,可以后向传播并使用梯度下降的形式更新权重值。再强调一次,通过前向传播数据查看权重的执行情况,然后向后传播数据以更新权重值,持续该过程,直到误差值达到最小(希望是全局最小值而非局部最小值)。重复训练!

1.7 小结

在本章中,我们简要概述了各种神经网络术语,回顾了感知器、神经元和后向传播等。在下一章中,我们将直接编写一个完整的神经网络程序!
我们将讨论诸如神经网络训练、突触、神经元、前向传播、Sigmoid函数、后向传播和误差计算等主题。
所以,保持状态,下一章开始编码!

1.8 参考文献

带你读《C#神经网络编程》之一:快速预览

上一篇:Android项目实战(四十五):Usb转串口通讯(CH34xUARTDriver)


下一篇:不同session的history操作同步问题