Python神经网络:训练简单的分类器篇

网友投稿 501 2022-09-07


Python神经网络:训练简单的分类器篇

我们希望训练线性分类器,使其能够正确分类瓢虫或毛虫。在1.3节的图中,根据观察,我们知道要做到这一点,简单说来,就是要调整分界线的斜率,使其能够基于小虫的宽度和长度将两组点划分开来。

我们如何做到这一点呢?

我们无需研究一些最前沿的数学理论。让我们通过尝试摸着石头过河,使用这种方式,我们可以更好地了解数学。

我们确实需要一些可以借鉴的实例。为了简单化这项工作,下表显示了两个实例。


实例



宽度



长度



小虫



1



3.0



1.0



瓢虫



2



1.0



3.0



毛虫


我们有宽度为3.0和长度为1.0的一只小虫,我们知道这是瓢虫。我们还有长度较长(为3.0)、宽度较小(为1.0)的一只小虫,这是一条毛虫。

我们知道这组实例是正确的。这些实例帮助我们调整分类函数的斜率。用来训练预测器或分类器的真实实例,我们称为训练数据。

让我们绘制出这两个训练数据实例。通过观察数字列表或数字表格是不容易理解和感知数据的,而可视化数据有助于我们做到这一点。

宽度

让我们使用一条随机的分界线开始我们的讨论。回顾一下,在千米转换为英里预测器的实例中,我们有一个调整了参数的线性函数。此处,由于分界线是一条直线,因此我们也可以进行相同的处理:

y =Ax

由于严格来说,此处的直线不是一台预测器,因此我们有意使用名称y和x,而不使用名称长度和宽度。与先前我们将千米转换为英里不一样,这条直线不将宽度转换为长度。相反,它是一条分界线,是一台分类器。

你可能还注意到,y = Ax比完整的直线形式y =Ax+ B更简单。我们刻意让花园中小虫的场景尽可能简单。简单说来,非零值B意味着直线不经过坐标原点。但是在我们的场景中,B不为零并未有任何用途。

之前,我们看到参数A控制着直线的斜率。较大的A对应着较大的斜率。

让我们尝试从A = 0.25开始,分界线为y = 0.25x。在与训练数据的同一张图中,我们绘制这条直线,观察一下这是一种什么情况。

无需任何计算,我们可以观察到直线y = 0.25x不是一台很好的分类器。这条直线未将两种类型的小虫区分开来。由于瓢虫也处在直线之上,因此我们不能说“如果小虫在直线之上,则这是一条毛虫”。

直观上,我们观察到需要将直线向上移动一点,但是我们要抵制诱惑,不能通过观察图就画出一条合适的直线。我们希望能够找到一种可重复的方法,也就是用一系列的计算机指令来达到这个目标。计算机科学家称这一系列指令为算法(algorithm)。

让我们观察第一个训练样本数据:宽度为3.0和长度为1.0瓢虫。如果我们使用这个实例测试函数y=Ax,其中x为3.0,我们得到:

y =0.25*3.0= 0.75

在这个函数中,我们将参数A设置为初始随机选择的值0.25,表明对于宽度为3.0的小虫,其长度应为0.75。但是,由于训练数据告诉我们这个长度必须为1.0,因此我们知道这个数字太小了。

现在,我们有了一个误差值。正如先前将千米转换为英里的预测器实例一样,我们可以利用这个误差值来搞清楚如何调整参数A。

但是,在我们调整参数A之前,让我们考虑y应该是什么值。如果y为1.0,那么直线就会恰好经过瓢虫所在的坐标点(x,y)=(3.0,1.0)。这是一个非常微妙的点,但是实际上,我们并不希望出现这种情况。我们希望直线处于这个点上方。为什么呢?因为我们希望所有瓢虫的点处于直线下方,而不是在直线上。这条直线需要成为瓢虫和毛虫之间的一条分界线,而不是给定小虫宽度、预测小虫长度的一个预测器。

因此,当x = 3.0时,我们尝试使用y=1.1的目标值。这只是比1.0大一点的数。我们也可以选择1.2甚至1.3,但是我们不希望使用10或100这样较大的数字,因为这很可能会使得直线在瓢虫和毛虫上方,导致这个分类器没有一点作用。

因此,期望的目标值是1.1,误差值E为

误差值=(期望目标值-实际输出值)

这样

E = 1.1-0.75 = 0.35

让我们暂停下来提醒一下自己,将误差值、期望的目标值和计算值的意义在图上表示出来。

现在,我们需要对这个E做些什么,才能更好地指导我们调整参数A呢?这是一个重要的问题。

在这个任务中,让我们退一步再想一想。我们希望用y中称为E的误差值,来搞清楚参数A所需改变的值。要做到这一点,我们需要知道两者的关系。A与E是如何关联的呢?如果我们知道了这一点,那么我们就可以理解更改一个值如何影响另一个值。

我们先从分类器的线性函数开始:

y =Ax

我们知道,A的初始猜测值给出了错误的y值,y值应该等于训练数据给定的值。我们将正确的期望值t称为目标值。为了得到t值,我们需要稍微调整A的值。数学家使用增量符号Δ表示“微小的变化量”。下面我们将这个变化量写出来:

t = (A + ΔA)x

让我们在图中将其画出来,以使其更容易理解。在图中,你可以看到新的斜率(A+ΔA)。

请记住,误差值E是期望的正确值与基于A的猜测值计算出来的值之间的差值。也就是说,E等于t-y。

我们将这个过程写出来,这样就清楚了:

t-y = (A + ΔA)x- Ax

展开表达式并化简:

E=t-y = (A + ΔA)x- Ax

E = (ΔA)x

这是多么美妙啊!误差值E与ΔA存在着一种简单的关系。这种关系如此简单,以至于我认为这是错的,但实际上这是正确的。无论如何,这种简单的关系让我们的工作变得相对容易。

我们很容易沉迷于代数,或由于代数而分心。让我们提醒自己,我们所希望的是摆脱这些代数,用一些简明的语言达到我们的目标。

根据误差值E,我们希望知道需要将A调整多少,才能改进直线的斜率,得到一台更好的分类器。要做到这一点,我们只要重新调整上一个方程,将 ΔA算出:

ΔA= E / x

这就可以了!这就是我们一直在寻找的神奇表达式。我们可以使用误差值E,将所得到的ΔA作为调整分界线斜率A的量。

让我们开始吧 ——更新最初的斜率。

误差值为0.35,x为3.0。这使得ΔA = E / x = 0.35 / 3.0 = 0.1167。这意味着当前的A = 0.25需要加上0.1167。这也意味着,修正后的A值为(A +ΔA),即0.25 + 0.1167 = 0.3667。当A=0.3667时,使用这个A值计算得到的y值为1.1,正如你所期望的,这就是我们想要的目标值。

唷!我们做到了!我们找到了基于当前的误差值调整参数的方法。

让我们继续前进吧!

现在,我们已经完成了一个实例训练,让我们从下一个实例中学习。此时,我们已知正确值对为x = 1.0和y = 3.0。

当线性函数使用更新后的A = 0.3667,并把x = 1.0代入到线性函数中时,让我们观察会发生什么情况。我们得到y = 0.3667 * 1.0 = 0.3667。这与训练样本中y = 3.0相去甚远。

基于与先前同样的推理,我们希望直线不要经过训练数据,而是稍微高于或低于训练数据,我们将所需的目标值设置为2.9。这样,毛虫的训练样本就在直线上方,而不是在直线之上。误差值E为2.9-0.3667= 2.5333。

比起先前,这个误差值更大,但是如果仔细想想,迄今为止,我们只使用一个单一的训练样本对线性函数进行训练,很明显,这使得直线偏向于这个单一的样本。

与我们先前所做的一样,让我们再次改进A。ΔA为E / x,即2.5333 / 1.0 = 2.5333。这意味着较新的A为0.3667 + 2.5333 = 2.9。这也意味着,对于x = 1.0,函数得出了2.9的答案,这正是所期望的值。

这个训练量有点大了,因此,让我们再次暂停,观察我们已经完成的内容。下图显示出了初始直线、向第一个训练样本学习后的改进直线和向第二个训练样本学习后的最终直线。

等等,这是什么情况啊!看着这幅图,我们似乎并没有做到让直线以我们所希望的方式倾斜。这条直线没有整齐地划分出瓢虫和毛虫。

好了,我们理解了先前的诉求。改进直线,以得出各个所需的y值。

这种想法有什么错误呢?如果我们继续这样操作,使用各个训练数据样本进行改进,那么我们所得到的是,最终改进的直线与最后一次训练样本非常匹配。实际上,最终改进的直线不会顾及所有先前的训练样本,而是抛弃了所有先前训练样本的学习结果,只是对最近的一个实例进行了学习。

如何解决这个问题呢?

其实很简单!在机器学习中,这是一个重要的思路。我们应该进行适度改进(moderate)。也就是说,我们不要使改进过于激烈。我们采用ΔA几分之一的一个变化值,而不是采用整个ΔA,充满激情地跳跃到每一个新的A值。使用这种方法,我们小心谨慎地向训练样本所指示的方向移动,保持先前训练迭代周期中所得到的值的一部分。在先前相对简单的千米转换为英里的预测器中,我们就已经观察到这种有节制的调整,我们小心翼翼地调整参数C,使其只是实际误差值的几分之几。

这种自我节制的调整,还带来了一个非常强大、行之有效的“副作用”。当训练数据本身不能确信为完全正确并且包含在现实世界测量中普遍出现的错误或噪声这两种情况时,有节制的调整可以抑制这些错误或噪声的影响。这种方法使得错误或噪声得到了调解和缓和。

好吧,让我们重新使用这种方法。但是这一次,在改进公式中,我们将添加一个调节系数:

ΔA= L(E / x)

调节系数通常被称为学习率(learning rate),在此,我们称之为L。我们就挑L = 0.5作为一个合理的系数开始学习过程。简单说来,这就意味着我们只更新原更新值的一半。

再一次重复上述过程,我们有一个初始值A = 0.25。使用第一个训练样本,我们得到y = 0.25 * 3.0 = 0.75,期望值为1.1,得到了误差值0.35。ΔA = L(E / x)= 0.5 * 0.35 / 3.0 = 0.0583。更新后的A值为0.25 + 0.0583 = 0.3083。

尝试使用新的A值计算训练样本,在x = 3.0时,得到y = 0.3083 * 3.0 = 0.9250。现在,由于这个值小于1.1,因此这条直线落在了训练样本错误的一边,但是,如果你将这视为后续的众多调整步骤的第一步,则这个结果不算太差。与初始直线相比,这条直线确实向正确方向移动了。

我们继续使用第二个训练数据实例,x = 1.0。使用A = 0.3083,我们得到y = 0.3083 * 1.0 = 0.3083。所需值为2.9,因此误差值是2.9-0.3083= 2.5917。ΔA = L(E / x) = 0.5 * 2.5917 / 1.0 = 1.2958。当前,第二个更新的值A等于0.3083 + 1.2958 = 1.6042。

让我们再次观察初始直线、改进后的直线和最终直线,观察这种有节制的改进是否在瓢虫和毛虫区域之间是否得到了更好的分界线。

结果真的很不错!

即使使用这两个简单的训练样本,利用带有调节学习速率的一种相对简单的改进方法,我们也非常迅速地得到了一条很好的分界线y =Ax,其中A为1.6042。

让我们先放下已经取得的成就。我们已经实现了自动化的学习方法,虽然方法非常简单,但卓有成效地对若干实例进行分类。

这太棒了!

关键点我们使用简单的数学,理解了线性分类器输出误差值和可调节斜率参数之间的关系。也就是说,我们知道了在何种程度上调整斜率,可以消除输出误差值。使用朴素的调整方法会出现一个问题,即改进后的模型只与最后一次训练样本最匹配,“有效地”忽略了所有以前的训练样本。解决这个问题的一种好方法是使用学习率,调节改进速率,这样单一的训练样本就不能主导整个学习过程。来自真实世界的训练样本可能充满噪声或包含错误。适度更新有助于限制这些错误样本的影响。

《Python神经网络编程》

本书是为了任何希望了解什么是神经网络的读者而编写的,是为了任何希望设计和使用自己神经网络的读者而编写的,也是为了任何希望领略那些在神经网络发挥核心作用、相对容易但激动人心的数学思想的读者而编写的。

本书的目标读者,不是数学或计算机科学方面的专家。你不需要任何专业知识或超出中学的数学能力。

如果你可以进行加、减、乘、除运算,那么你就可以制作自己的神经网络。我们使用的最困难运算是梯度演算(gradient calculus),但是,我们会对这一概念加以说明,使尽可能多的读者能够理解这个概念。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Springboot hibernate
下一篇:如何用Python画一只机器猫?(用python画夜空中的猫)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~