读书笔记(三)

即便对于复杂的函数,感知机也隐含着能够表示它的可能性
即便是计算机进行的复杂处理,感知机(理论上)也可以将其表示出来
然而,设定权重的工作,即确定合适的、能符合预期的输入与输出的权重,现在还是由人工进行的

神经网络的一个重要性质是它可以自动地从数据中学习到合适的权重参数
 

激活函数是连接感知机和神经网络的桥梁
 

一般而言,“朴素感知机”是指单层网络,指的是激活函数使用了阶跃函数 A 的模型。“多层感知机”是指神经网络,即使用sigmoid 
函数(后述)等平滑的激活函数的多层网络

上式表示的激活函数以阈值为界,一旦输入超过阈值,就切换输出。这样的函数称为“阶跃函数”
在激活函数的众多候选函数中,感知机使用了阶跃函数
 

sigmoid函数

感知机和神经网络的主要区别就在于这个激活函数
 

对NumPy数组进行不等号运算
>>> import numpy as np
>>> x = np.array([-1.0, 1.0, 2.0])
>>> x
array([-1., 1., 2.])
>>> y = x > 0
>>> y
array([False, True, True], dtype=bool)

 

>>> y = y.astype(np.int)
>>> y
array([0, 1, 1])

可以用 astype()方法转换NumPy数组的类型。 astype()方法通过参数指定期望的类型,这个例子中是 np.int型。 Python中将布尔型转换为 int型后, True会转换为1, False会转换为0
 

阶跃函数的图像

sigmoid函数的实现

要注意参数 x为NumPy数组时,结果也能被正确计算
实际上,如果在这个sigmoid函数中输入一个NumPy数组,则结果如下所示

之所以sigmoid函数的实现能支持NumPy数组,秘密就在于NumPy的广播功能(1.5.5节)。根据NumPy 的广播功能,如果在标量和NumPy数组之间进行运算,则标量会和NumPy数组的各个元素进行运算
>>> t = np.array([1.0, 2.0, 3.0])
>>> 1.0 + t
array([ 2., 3., 4.])
>>> 1.0 / t
array([ 1. , 0.5 , 0.33333333])
sigmoid函数的实现也是如此,因为 np.exp(-x)会生成NumPy数组,所以 1 / (1 + np.exp(-x))的运算将会在NumPy数组的各个元素间进行。
sigmoid函数的图像

sigmoid函数的平滑性对神经网络的学习具有重要意义

 

相对于阶跃函数只能返回0或1, sigmoid函数可以返回0.731 . . .、 0.880 . . .等实数(这一点和刚才的平滑性有关)。也就是说,感知机中神经元之间流动的是0或1的二元信号,而神经网络中流动的是连续的实数值信号
阶跃函数和sigmoid函数虽然在平滑性上有差异,但是如果从宏观视角看图3-8,可以发现它们具有相似的形状。实际上,两者的结构均是“输入小时,输出接近0(为0);随着输入增大,输出向1靠近(变成1)”。也就是说,当输入信号为重要信息时,阶跃函数和sigmoid函数都会输出较大的值;当输入信号为不重要的信息时,两者都输出较小的值。还有一个共同点是,不管输入信号有多小,或者有多大,输出信号的值都在0到1之间
 

阶跃函数和sigmoid函数还有其他共同点,就是两者均为非线性函数。sigmoid函数是一条曲线,阶跃函数是一条像阶梯一样的折线,两者都属于非线性的函数
 

函数本来是输入某个值后会返回一个值的转换器。向这个转换器输入某个值后,输出值是输入值的常数倍的函数称为线性函数(用数学式表示为h(x) = cx。 c为常数)。因此,线性函数是一条笔直的直线。而非线性函数,顾名思义,指的是不像线性函数那样呈现出一条直线的函数
 

神经网络的激活函数必须使用非线性函数。换句话说,激活函数不能使用线性函数。为什么不能使用线性函数呢?因为使用线性函数的话,加深神经网络的层数就没有意义了
线性函数的问题在于,不管如何加深层数,总是存在与之等效的“无隐藏层的神经网络”
这里我们考虑把线性函数 h(x) = cx 作为激活函数,把y(x) = h(h(h(x)))的运算对应3层神经网络 A。这个运算会进行y(x) = c × c × c × x的乘法运算,但是同样的处理可以由y(x) = ax(注意,a = c3)这一次乘法运算(即没有隐藏层的神经网络)来表示。如本例所示,使用线性函数时,无法发挥多层网络带来的优势。因此,为了发挥叠加层所带来的优势,激活函数必须使用非线性函数
 

ReLU函数

在神经网络发展的历史上, sigmoid函数很早就开始被使用了,而最近则主要使用ReLU(Rectifed Linear Unit)函数
ReLU函数在输入大于0时,直接输出该值;在输入小于等于0时,输出0

 

NumPy多维数组的运算

数组的维数可以通过 np.dim()函数获得,数组的形状可以通过实例变量 shape获得

A是一维数组,由4个元素构成。注意,这里的 A.shape的结果是个元组(tuple)。这是因为一维数组的情况下也要返回和多维数组的情况下一致的结果。例如,二维数组时返回的是元组 (4,3),三维数组时返回的是元组 (4,3,2),因此一维数组时也同样以元组的形式返回结果
二维数组

二维数组也称为矩阵(matrix)。数组的横向排列称为行(row),纵向排列称为列(column)。
矩阵乘法

 

各层间信号传递的实现

 

输出层所用的激活函数,要根据求解问题的性质决定。一般地,回归问题可以使用恒等函数,二元分类问题可以使用 sigmoid函数,多元分类问题可以使用 softmax函数
 

神经网络可以用在分类问题和回归问题上,不过需要根据情况改变输出层的激活函数。一般而言,回归问题用恒等函数,分类问题用softmax函数
 

机器学习的问题大致可以分为分类问题和回归问题。分类问题是数据属于哪一个类别的问题。比如,区分图像中的人是男性还是女性的问题就是分类问题。而回归问题是根据某个输入预测一个(连续的)数值的问题。比如,根据一个人的图像预测这个人的体重的问题就是回归问题(类似“57.4kg”这样的预测)
 

恒等函数会将输入按原样输出,对于输入的信息,不加以任何改动地直接输出。因此,在输出层使用恒等函数时,输入信号会原封不动地被输出。

 

softmax函数

exp(x)是表示ex的指数函数(e是纳皮尔常数2.7182 . . .)
上式表示假设输出层共有n个神经元,计算第k个神经元的输出yk。如上式所示,softmax函数的分子是输入信号ak的指数函数,分母是所有输入信号的指数函数的和
 

实现softmax函数时的注意事项

上面的 softmax函数的实现虽然正确描述了式(3.10),但在计算机的运算上有一定的缺陷。这个缺陷就是溢出问题。 softmax函数的实现中要进行指数函数的运算,但是此时指数函数的值很容易变得非常大。如e1000的结果会返回一个表示无穷大的 inf。如果在这些超大值之间进行除法运算,结果会出现“不确定”的情况。

 

计算机处理“数”时,数值必须在4字节或8字节的有限数据宽度内。这意味着数存在有效位数,也就是说,可以表示的数值范围是有限的。因此,会出现超大值无法表示的问题。这个问题称为溢出,在进行计算机的运算时必须(常常)注意
 

改进版本的softmax函数

上式说明,在进行softmax的指数函数的运算时,加上(或者减去)某个常数并不会改变运算的结果。这里的C 可以使用任何值,但是为了防止溢出,一般会使用输入信号中的最大值

 

softmax函数的特征

softmax函数的输出是0.0到1.0之间的实数。并且, softmax函数的输出值的总和是1。输出总和为1是softmax函数的一个重要性质。正因为有了这个性质,我们才可以把softmax函数的输出解释为“概率”。

 

通过使用softmax函数,我们可以用概率的(统计的)方法处理问题
注意:使用了softmax函数,各个元素之间的大小关系也不会改变。这是因为指数函数(y = exp(x))是单调递增函数。实际上,
上例中 a的各元素的大小关系和 y的各元素的大小关系并没有改变。

 

一般而言,神经网络只把输出值最大的神经元所对应的类别作为识别结果。并且,即便使用softmax函数,输出值最大的神经元的位置也不会变。因此,神经网络在进行分类时,输出层的softmax函数可以省略。在实际的问题中,由于指数函数的运算需要一定的计算机运算量,因此输出层的softmax函数一般会被省略
 

求解机器学习问题的步骤可以分为“学习”A 和“推理”两个阶段。首先, 在学习阶段进行模型的学习 B,然后,在推理阶段,用学到的模型对未知的数据进行推理(分类)。如前所述,推理阶段一般会省略输出层的 softmax 函数。在输出层使用 softmax 函数是因为它和神经网络的学习有关系
 

输出层的神经元数量需要根据待解决的问题来决定。对于分类问题,输出层的神经元数量一般设定为类别的数量

 

假设学习已经全部结束,我们使用学习到的参数,先实现神经网络的“推理处理”。这个推理处理也称为神经网络的前向
传播(forward propagation)。
 

和求解机器学习问题的步骤(分成学习和推理两个阶段进行)一样,使用神经网络解决问题时,也需要首先使用训练数据(学习数据)进行权重参数的学习;进行推理时,使用刚才学习到的参数,对输入数据进行分类。
 

将图像的各个像素值除以255,使得数据的值在0.0~1.0的范围内。像这样把数据限定到某个范围内的处理称为正规化(normalization)。此外,对神经网络的输入数据进行某种既定的转换称为预处理(pre-processing)。这里,作为对输入图像的一种预处理,我们进行了正规化
 

预处理在神经网络(深度学习)中非常实用,其有效性已在提高识别性能和学习的效率等众多实验中得到证明。在刚才的例子中,作为一种预处理,我们将各个像素值除以 255,进行了简单的正规化。实际上,很多预处理都会考虑到数据的整体分布。比如,利用数据整体的均值或标准差,移动数据,使数据整体以 0为中心分布,或者进行正规化,把数据的延展控制在一定范围内。除此之外,还有将数据整体的分布形状均匀化的方法,即数据白化(whitening)等。
 

从整体的处理流程来看,图3-26中,输入一个由784个元素(原本是一个28 × 28的二维数组)构成的一维数组后,输出一个有10个元素的一维数组。这是只输入一张图像数据时的处理流程。
现在我们来考虑打包输入多张图像的情形。比如,我们想用 predict()函数一次性打包处理100张图像。为此,可以把x的形状改为100 × 784,将100张图像打包作为输入数据。用图表示的话,如图3-27所示。

如图 3-27 所示,输入数据的形状为 100 × 784,输出数据的形状为100 × 10。这表示输入的100张图像的结果被一次性输出了。比如, x[0]和y[0]中保存了第0张图像及其推理结果, x[1]和 y[1]中保存了第1张图像及其推理结果,等等。
这种打包式的输入数据称为批(batch)
 

批处理对计算机的运算大有利处,可以大幅缩短每张图像的处理时间。那么为什么批处理可以缩短处理时间呢?这是因为大多数处理数值计算的库都进行了能够高效处理大型数组运算的最优化。并且,在神经网络的运算中,当数据传送成为瓶颈时,批处理可以减轻数据总线的负荷(严格地讲,相对于数据读入,可以将更多的时间用在计算上)。也就是说,批处理一次性计算大型数组要比分开逐步计算各个小型数组速度更快
 

>>> list( range(0, 10) )
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> list( range(0, 10, 3) )
[0, 3, 6, 9]
 

>>> x = np.array([[0.1, 0.8, 0.1], [0.3, 0.1, 0.6],
... [0.2, 0.5, 0.3], [0.8, 0.1, 0.1]])
>>> y = np.argmax(x, axis=1)
>>> print(y)
[1 2 1 0]
我们给定了参数 axis=1。这指定了在100 × 10的数组中,沿着第1维方向(以第1维为轴)找到值最大的元素的索引(第0维对应第1个维度)

 

NumPy数组之间使用比较运算符(==)生成由 True/False构成的布尔型数组,并计算 True的个数
>>> y = np.array([1, 2, 1, 0])
>>> t = np.array([1, 2, 0, 0])
>>> print(y==t)
[True True False True]
>>> np.sum(y==t)
3
使用批处理,可以实现高速且高效的运算。
输入数据的集合称为批。通过以批为单位进行推理处理,能够实现高速的运算。
 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值