基本是《深度学习入门-基于Python的理论与实现》的复制粘贴,以作为日后的检索和查询使用
感知机
感知机接收多个输入信号,输出一个信号。
感知机原理
感知机接收多个输入信号,输出一个信号。 图2-1是一个接收两个输入信号的感知机的例子。 x1、 x2是输入信号,y是输出信号, w1、 w2是权重。
图2-1 有两个输入的感知机
感知机公式:
y
=
{
0
(
w
1
x
1
+
w
2
x
2
⩽
θ
)
1
(
w
1
x
1
+
w
2
x
2
>
θ
)
(
2.1
)
y=\begin{cases}0&(w_1x_1+w_2x_2\leqslant\theta)\\1&(w_1x_1+w_2x_2>\theta)\end{cases}\quad(2.1)
y={01(w1x1+w2x2⩽θ)(w1x1+w2x2>θ)(2.1)
实现与门
输入:
x1 | x2 | y |
---|---|---|
0 | 0 | 0 |
1 | 0 | 0 |
0 | 1 | 0 |
1 | 1 | 1 |
满足图2-2的条件的参数的选择方法有无数多个。比如,当(w1, w2, θ) = (0.5, 0.5, 0.7) 时,可以满足图 2-2 的条件。此外,当 (w1, w2, θ)为(0.5, 0.5, 0.8)或者(1.0, 1.0, 1.0)时,同样也满足与门的条件。设定这样的参数后,仅当x1和x2同时为1时,信号的加权总和才会超过给定的阈值θ。
实现与非门
x1 | x2 | y |
---|---|---|
0 | 0 | 1 |
1 | 0 | 1 |
0 | 1 | 1 |
1 | 1 | 0 |
表示与非门,可以用(w1, w2, θ) = (-0.5, -0.5, -0.7)这样的组合(其
他的组合也是无限存在的)。
实现或门
x1 | x2 | y |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
1 | 1 | 1 |
感知机实现
与门实现
def AND(x1, x2):
w1, w2, theta = 0.5, 0.5, 0.7
tmp = x1 * w1 + x2 * w2
if tmp <= theta:
return 0
elif tmp > theta:
return 1
导入权重和偏置
为了后续的拓展与使用,改成另一种表现形式。把式(2.1)的θ换成-b,于是就可以用式(2.2)来表示感知机的行为
y = { 0 ( b + w 1 x 1 + w 2 x 2 ⩽ 0 ) 1 ( b + w 1 x 1 + w 2 x 2 > 0 ) ( 2.2 ) y=\begin{cases}0&(b+w_1x_1+w_2x_2\leqslant0)\\1&(b+w_1x_1+w_2x_2>0)\end{cases}\quad(2.2) y={01(b+w1x1+w2x2⩽0)(b+w1x1+w2x2>0)(2.2)
导入权重和偏置-与门的实现
def AND(x1, x2):
x = np.array([x1, x2])
w = np.array([0.5, 0.5])
b = -0.7
tmp = np.sum(w * x) + b
if tmp <= 0:
return 0
else:
return 1
w1和w2是控制输入信号的重要性的参数,而偏置是调整神经元被激活的容易程度(输出信号为1的程度)的参数。 -θ命名为偏置b,若b为-0.1,则只要输入信号的加权总和超过0.1,神经元就会被激活。但是如果b为-20.0,则输入信号的加权总和必须超过20.0,神经元才会被激活。
导入权重和偏执-与非门的实现
def NAND(x1, x2):
x = np.array([x1, x2])
w = np.array([-0.5, -0.5]) # 仅权重和偏置与AND不同!
b = 0.7
tmp = np.sum(w * x) + b
if tmp <= 0:
return 0
else:
return 1
导入权重和偏执-或门的实现
def OR(x1, x2):
x = np.array([x1, x2])
w = np.array([0.5, 0.5]) # 仅权重和偏置与AND不同!
b = -0.2
tmp = np.sum(w * x) + b
if tmp <= 0:
return 0
else:
return 1
感知机的局限性
异或门
x1 | x2 | y |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
0 | 1 | 1 |
1 | 1 | 0 |
如图所示,圆形代表0,三角形代表1,我们不能使用一条直线将0和1分开
线性和非线性
由曲线分割而成的空间称为非线性空间,由直线分割而成的空间称为线性空间。
多层感知机
感知机的绝妙之处在于它可以“叠加层”,进而可以实现本部可以直接实现的异或门
x1 | x2 | s1 | s2 | y |
---|---|---|---|---|
0 | 0 | 1 | 0 | 0 |
1 | 0 | 1 | 1 | 1 |
0 | 1 | 1 | 1 | 1 |
1 | 1 | 0 | 1 | 0 |
异或门的实现
def XOR(x1, x2):
s1 = NAND(x1, x2)
s2 = OR(x1, x2)
y = AND(s1, s2)
return y
多层感知机
与门、或门是单层感知机,异或门是2层感知机 。叠加了多层的感知机也称为多层感知机 。
神经网络
关于感知机,即便对于复杂的函数,感知机也隐含着能够表示它的可能性。
但是,设定权重的工作,即确定合适的、能符合预期的输入与输出的权重,现在还是由人工进行的。
神经网络的出现就是为了解决这个问题,神经网络的一个重要性质是它可以自动地从数据中学习到合适的权重参数。
从感知机到神经网络
神经网络是什么样的
用图来表示神经网络的话,如图3-1所示。最左边的一列称为输入层,最右边的一列称为输出层,中间的一列称为中间层(隐藏层)。
输入层到输出层依次称为第0层、第1层、第2层。图3-1中,第0层对应输入层,第1层对应中间层,第2层对应输出层。
对于感知机,如果要明确地表示出b,可以像图3-3那样做。图3-3中添加了权重为b的输入信号1。这个感知机将x1、 x2、 1三个信号作为神经元的输入,将其和各自的权重相乘后,传送至下一个神经元。在下一个神经元中,计算这些加权信号的总和。如果这个总和超过0,则输出1,否则输出0。另外,由于偏置的输入信号一直是1,所以为了区别于其他神经元,我们在图中把这个神经元整个涂成灰色。
感知机
使用感知机来接收x1和x2两个输入信号,输出y。用数学式来表示如式(3.1)所示。
y
=
{
0
(
b
+
w
1
x
1
+
w
2
x
2
⩽
0
)
1
(
b
+
w
1
x
1
+
w
2
x
2
>
0
)
(
3.1
)
y=\begin{cases}0&(b+w_1x_1+w_2x_2\leqslant0)\\1&(b+w_1x_1+w_2x_2>0)\end{cases}\quad(3.1)
y={01(b+w1x1+w2x2⩽0)(b+w1x1+w2x2>0)(3.1)
加入偏置b
我们用一个函数来表示这种分情况的动作(超过0则输出1,否则输出0)。引入新函数h(x)。
y
=
h
(
b
+
w
1
x
1
+
w
2
x
2
)
(
3.2
)
y=h(b+w_1x_1+w_2x_2)\quad(3.2)
y=h(b+w1x1+w2x2)(3.2)
h
(
x
)
=
{
0
(
x
⩽
0
)
1
(
x
>
0
)
(
3.3
)
h(x)=\begin{cases}0&(x\leqslant0)\\1&(x>0)\end{cases}\quad(3.3)
h(x)={01(x⩽0)(x>0)(3.3)
在y中,输入信号的总和会被函数h(x)转换,转换后的值就是输出y。h(x)表示的函数,在输入超过0时返回1,否则返回0。
使用激活函数
h(x) 函数会将输入信号的总和转换为输出信号,这种函数一般称为激活函数,激活函数的作用在于决定如何来激活输入信号的总和。
现在来进一步改写式(3.2)。式(3.2)分两个阶段进行处理,先计算输入信号的加权总和,然后用激活函数转换这一总和。因此,如果将式(3.2)写得详细一点,则可以分成下面两个式子。 首先,式(3.4)计算加权输入信号和偏置的总和,记为a。然后,式(3.5)用h()函数将a转换为输出y。
a
=
b
+
w
1
x
1
+
w
2
x
2
(
3.4
)
y
=
h
(
a
)
(
3.5
)
\begin{aligned}a&=b+w_1x_1+w_2x_2&(3.4)\\[2ex]y&=h(a)&(3.5)\end{aligned}
ay=b+w1x1+w2x2=h(a)(3.4)(3.5)
通常如图3-5的左图所示,神经元用一个○表示。本书中,在可以明确神经网络的动作的情况下,将在图中明确显示激活函数的计算过程,如图3-5的右图所示。
图3-5 左图是一般的神经元的图,右图是在神经元内部明确显示激活函数的计算过程的图(a表示输入信号的总和, h()表示激活函数, y表示输出)
激活函数
如式(3.3)以阈值为界,输入超过阈值,就切换输出,这样的函数称为“阶跃函数”。感知机使用的就是阶跃函数。更换了其他函数之后,就可以进神经网络了。
阶跃函数只能返回0或1, sigmoid函数可以返回任意小数等实数
sigmoid函数
神经网络中经常使用的一个激活函数就是式(3.6)表示的sigmoid函数
h
(
x
)
=
1
1
+
exp
(
−
x
)
(
3.6
)
h(x)=\dfrac{1}{1+\exp(-x)}\quad(3.6)
h(x)=1+exp(−x)1(3.6)
exp(-x)表示e-x的意思。
实现阶跃函数
import numpy as np
import matplotlib.pylab as plt
def step_function(x):
return np.array(x > 0, dtype=np.int_)
x = np.arange(-5.0, 5.0, 0.1)
y = step_function(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) # 指定y轴的范围
plt.show()
实现sigmoid函数
import numpy as np
import matplotlib.pylab as plt
def sigmoid(x):
return 1 / (1 + np.exp(-x))
x = np.arange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1) # 指定y轴的范围
plt.show()
非线性函数
阶跃函数和sigmoid函数还有其他共同点,就是两者均为非线性函数。
神经网络的激活函数必须使用非线性函数。 使用线性函数的话,加深神经网络的层数就没有意义了。
线性函数的问题在于,不管如何加深层数,总是存在与之等效的“无隐藏层的神经网络”。
如果使用线性函数 h(x) = cx 作为激活函数,把y(x) = h(h(h(x)))的运算对应3层神经网络 A。这个运算会进行y(x) = c × c × c × x的乘法运算,但是同样的处理可以由y(x) = ax(注意,a = c3)这一次乘法运算(即没有隐藏层的神经网络)来表示。此时无法发挥多层网络带来的优势。
因此,为了发挥叠加层所带来的优势,激活函数必须使用非线性函数。
ReLU函数
ReLU函数在输入大于0时,直接输出该值;在输入小于等于0时,输出0(图3-9)。
h
(
x
)
=
{
x
(
x
>
0
)
0
(
x
⩽
0
)
(
3.7
)
h(x)=\begin{cases}x&(x>0)\\0&(x\leqslant0)\end{cases}\quad(3.7)
h(x)={x0(x>0)(x⩽0)(3.7)
实现:
import numpy as np
import matplotlib.pyplot as plt
# 定义ReLU函数
def ReLU(x):
return np.maximum(0, x)
x = np.linspace(-5, 5, 100)
y = ReLU(x)
plt.plot(x, y, linewidth=2)
plt.xlabel('x')
plt.ylabel('ReLU(x)')
plt.title('ReLU Function')
plt.show()
3层神经网络的实现
用数学公式表示a1(1),通过加权信号和偏置,按如下方式进行计算:
a
1
(
1
)
=
w
11
(
1
)
x
1
+
w
12
(
1
)
x
2
+
b
1
(
1
)
(
3.8
)
a_1^{(1)}=w_{11}^{(1)}x_1+w_{12}^{(1)}x_2+b_1^{(1)}\quad(3.8)
a1(1)=w11(1)x1+w12(1)x2+b1(1)(3.8)
使用矩阵乘法,可以将第1层的加权和表示成小面的式(3.9)
A
(
1
)
=
X
W
(
1
)
+
B
(
1
)
(
3.9
)
A^{(1)}=XW^{(1)}+B^{(1)}\quad(3.9)
A(1)=XW(1)+B(1)(3.9)
其中, A(1)、X、B(1)、W(1)如下所示
A
(
1
)
=
(
a
1
(
1
)
a
2
(
1
)
a
3
(
1
)
)
,
X
=
(
x
1
x
2
)
,
B
(
1
)
=
(
b
1
(
1
)
b
2
(
1
)
b
3
(
1
)
)
W
(
1
)
=
(
w
11
(
1
)
w
21
(
1
)
w
31
(
1
)
w
12
(
1
)
w
22
(
1
)
w
32
(
1
)
)
\begin{aligned} &A^{(1)} =\begin{pmatrix}a_{1}^{(1)}&a_{2}^{(1)}&a_{3}^{(1)}\end{pmatrix},\boldsymbol{X}=\begin{pmatrix}x_{1}&x_{2}\end{pmatrix},\boldsymbol{B}^{(1)}=\begin{pmatrix}b_{1}^{(1)}&b_{2}^{(1)}&b_{3}^{(1)}\end{pmatrix} \\ &W(1) =\begin{pmatrix}w_{11}^{(1)}&w_{21}^{(1)}&w_{31}^{(1)}\\w_{12}^{(1)}&w_{22}^{(1)}&w_{32}^{(1)}\end{pmatrix} \end{aligned}
A(1)=(a1(1)a2(1)a3(1)),X=(x1x2),B(1)=(b1(1)b2(1)b3(1))W(1)=(w11(1)w12(1)w21(1)w22(1)w31(1)w32(1))
使用NumPy多维数组来实现:
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def identity_function(x):
return x
输入层 -> 第一层
X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])
print(W1.shape) # (2, 3)
print(X.shape) # (2,)
print(B1.shape) # (3,)
A1 = np.dot(X, W1) + B1
Z1 = sigmoid(A1)
print(A1) # [0.3, 0.7, 1.1]
print(Z1) # [0.57444252, 0.66818777, 0.75026011]
第一层 -> 第二层
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)
print(A2) # [0.51615984 1.21402696]
print(Z2) # [0.62624937 0.7710107 ]
第二层 -> 输出层
W3 = np.array([[0.1, 0.3], [0.2, 0.4]])
B3 = np.array([0.1, 0.2])
A3 = np.dot(Z2, W3) + B3
Y = identity_function(A3) # 或者Y = A3
print(A3) # [0.31682708 0.69627909]
print(Y) # [0.31682708 0.69627909]
代码小结
import numpy as np
def sigmoid(x):
return 1 / (1 + np.exp(-x))
def identity_function(x):
return x
def init_network():
network = {}
network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
network['b1'] = np.array([0.1, 0.2, 0.3])
network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
network['b2'] = np.array([0.1, 0.2])
network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
network['b3'] = np.array([0.1, 0.2])
return network
def forward(network, x):
W1, W2, W3 = network['W1'], network['W2'], network['W3']
b1, b2, b3 = network['b1'], network['b2'], network['b3']
a1 = np.dot(x, W1) + b1
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(z2, W3) + b3
y = identity_function(a3)
return y
network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)
print(y) # [ 0.31682708 0.69627909]
输出层的设计
神经网络可以用在分类问题和回归问题上,不过需要根据情况改变输出层的激活函数。一般而言,回归问题用恒等函数,分类问题用softmax函数。
恒等函数和softmax函数
恒等函数会将输入按原样输出,对于输入的信息,不加以任何改动地直接输出。在输出层使用恒等函数时,输入信号会原封不动地被输出。
分类问题中使用的softmax函数可以用下面的式(3.10)表示。
y
k
=
exp
(
a
k
)
∑
i
=
1
n
exp
(
a
i
)
(
3.10
)
\begin{aligned}y_k&=\frac{\exp(a_k)}{\sum\limits_{i=1}^n\exp(a_i)}&(3.10)\end{aligned}
yk=i=1∑nexp(ai)exp(ak)(3.10)
假设输出层共有n个神经元,计算第k个神经元的输出yk。softmax函数的分子是输入信号ak的指数函数,分母是所有输入信号的指数函数的和。softmax函数的输出通过箭头与所有的输入信号相连。
实现softmax函数
import numpy as np
def softmax(a):
exp_a = np.exp(a)
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
a = np.array([0.3, 2.9, 4.0])
y = softmax(a)
print(y)
实现softmax函数时要注意ex溢出的问题
改造softmax函数
y
k
=
exp
(
a
k
)
∑
i
=
1
n
exp
(
a
i
)
=
C
exp
(
a
k
)
C
∑
i
=
1
n
exp
(
a
i
)
=
exp
(
a
k
+
log
C
)
∑
i
=
1
n
exp
(
a
i
+
log
C
)
(
3.11
)
=
exp
(
a
k
+
C
′
)
∑
i
=
1
n
exp
(
a
i
+
C
′
)
\begin{aligned} y_{k}={\frac{\exp(a_{k})}{\sum_{i=1}^{n}\exp(a_{i})}}& =\frac{\mathrm{C}\exp(a_{k})}{\mathrm{C}\sum_{i=1}^{n}\exp(a_{i})} \\ &=\frac{\exp(a_k+\log\text{C})}{\sum\limits_{i=1}^n\exp(a_i+\log\text{C})}& (\begin{matrix}3.11\\\end{matrix}) \\ &=\frac{\exp(a_k+\mathrm{C'})}{\sum\limits_{i=1}^n\exp(a_i+\mathrm{C'})} \end{aligned}
yk=∑i=1nexp(ai)exp(ak)=C∑i=1nexp(ai)Cexp(ak)=i=1∑nexp(ai+logC)exp(ak+logC)=i=1∑nexp(ai+C′)exp(ak+C′)(3.11)
首先,式(3.11)在分子和分母上都乘上C这个任意的常数(因为同时对分母和分子乘以相同的常数,所以计算结果不变)。然后,把这个C移动到指数函数(exp)中,记为logC。最后,把log C替换为另一个符号C’。
实现改造的softmax函数
import numpy as np
def softmax(a):
c = np.max(a)
exp_a = np.exp(a - c) # 溢出对策
sum_exp_a = np.sum(exp_a)
y = exp_a / sum_exp_a
return y
a = np.array([0.3, 2.9, 4.0])
y = softmax(a)
print(y) # [0.01821127 0.24519181 0.73659691]
softmax函数的输出是0.0到1.0之间的实数,且输出值的总和是1,所以我们把softmax函数的输出解释为“概率”。
一般神经网络只把输出值最大的神经元所对应的类别作为识别结果。并且,即便使用softmax函数,输出值最大的神经元的位置也不会变。在实际的问题中,由于指数函数的运算需要一定的计算机运算量,神经网络在进行分类时,输出层的softmax函数一般都会省略。
输出层的神经元数量需要根据待解决的问题来决定。 对于分类问题,输出层的神经元数量一般设定为类别的数量。 比如,对于某个输入图像,数字0到9中的哪一个的问题,可以像图3-23这样,将输出层的神经元设定为10个。
手写数字识别
MNIST数据集
这里使用的数据集是MNIST手写数字图像集。 MNIST是机器学习领域最有名的数据集之一,被应用于从简单的实验到发表的论文研究等各种场合。实际上,在阅读图像识别或机器学习的论文时, MNIST数据集经常作为实验用的数据出现。
训练图像有6万张,测试图像有1万张。
神经网络的学习
数据驱动
机器学习的一切行为都来源于数据,数据是机器学习的核心,机器学习避免人为的介入, 尝试从收集到的数据中发现答案(模式)。
如何识别数字5?
- 人可以简单的识别出数字5,但是说不出来是通过一种怎样的模式来识别出来的。
- 考虑通过有效利用数据来解决这个问题,一种方案是, 先从图像中提取特征量, 再用机器学习技术学习这些特征量的模式。这里所说的“特征量”是指可以从输入数据(输入图像)中准确地提取本质数据(重要的数据)的转换器。图像的特征量通常表示为向量的形式。在计算机视觉领域,常用的特征量包括SIFT、SURF和HOG等。使用这些特征量将图像数据转换为向量,然后对转换后的向量使用机器学习中的SVM、KNN等分类器进行学习。
- 机器学习的方法中,由机器从收集到的数据中找出规律性。与从零开始想出算法相比,这种方法可以更高效地解决问题,也能减轻人的负担。但是需要注意的是,将图像转换为向量时使用的特征量仍是由人设计的。对于不同的问题,必须使用合适的特征量(必须设计专门的特征量),才能得到好的结果。比如,为了区分狗的脸部,人们需要考虑与用于识别5的特征量不同的其他特征量。也就是说,即使使用特征量和机器学习的方法,也需要针对不同的问题人工考虑合适的特征量。
训练数据:学习使用的数据,用来寻找最优的参数
测试(监督)数据:用来评价训练得到的模型的实际能力的数据
测试数据求的是模型的泛化能力,也可以叫做监督数据
损失函数
损失函数:“恶劣程度”的指标,即当前的神经网络对监督数据在多大程度上不拟合。
均方误差
E
=
1
2
∑
k
(
y
k
−
t
k
)
2
(
4.1
)
E=\frac{1}{2}\sum_{k}(y_{k}-t_{k})^{2}\quad\quad\quad\quad\quad(4.1)
E=21∑k(yk−tk)2(4.1)
yk是标识神经网络的输出,tk标识监督数据,k标识数据的维数。
交叉熵误差
E
=
−
∑
k
t
k
log
y
k
(
4.2
)
E=-\sum_kt_k\log y_k\quad\quad\quad\quad\quad(4.2)
E=−∑ktklogyk(4.2)
log表示以e为底数的自然对数(loge)。 yk是神经网络的输出,tk是正确解标签。 并且,tk中只有正确解标签的索引为1,其他均为0(one-hot表示)。因此,式(4.2)实际上只计算对应正确解标签的输出的自然对数。 交叉熵误差的值是由正确解标签所对应的输出结果决定的。
mini-batch学习
均方误差和交叉熵误差损失函数考虑的都是针对单个数据的损失函数。如果要求所有训练数据的损失函数的总和,以交叉熵误差为例,可以写成下面的式(4_.3)。
E
=
−
1
N
∑
n
∑
k
t
n
k
log
y
n
k
(
4.3
)
E=-\frac{1}{N}\sum_{n}\sum_{k}t_{nk}\log ynk\quad\quad\quad\quad(4.3)
E=−N1∑n∑ktnklogynk(4.3)
假设数据有N个, tnk表示第n个数据的第k个元素的值(ynk是神经网络的输出, tnk是监督数据)。
把求单个数据的损失函数的式(4.2)扩大到了_N_份数据,不过最后还要除以_N_进行正规化。通过除以_N,可以求单个数据的“平均损失函数”。通过这样的平均化,可以获得和训练数据的数量无关的统一指标。比如,即便训练数据有1000个或10000个,也可以求得单个数据的平均损失函数。 即便训练数据有1000个或10000个,也可以求得单个数据的平均损失函数。
为何要设定损失函数
在神经网络的学习中,寻找最优参数(权重和偏置)时,要寻找使损失函数的值尽可能小的参数。为了找到使损失函数的值尽可能小的地方,需要计算参数的导数(确切地讲是梯度),然后以这个导数为指引,逐步更新参数的值。
假设有一个神经网络,现在我们关注这个神经网络中的某一个权重参数。此时,对该权重参数的损失函数求导,表示的是“如果稍微改变这个权重参数的值,损失函数的值会如何变化”。如果导数的值为负,通过使该权重参数向正方向改变,可以减小损失函数的值;反过来,如果导数的值为正,则通过使该权重参数向负方向改变,可以减小损失函数的值。不过,当导数的值为0时,无论权重参数向哪个方向变化,损失函数的值都不会改变,此时该权重参数的更新会停在此处。
**在进行神经网络的学习时,不能将识别精度作为指标。因为如果以识别精度为指标,则参数的导数在绝大多数地方都会变为0。 **
识别精度对微小的参数变化基本上没有什么反应,即便有反应,它的值也是不连续地、突然地变化。
神经网络的学习无法使用阶跃函数作为激活函数,因为阶跃函数的导数在绝大多数地方(除了0以外的地方)均为0。
而sigmoid函数,不仅函数的输出(竖轴的值)是连续变化的,曲线的斜率(导数)也是连续变化的。也就是说, sigmoid函数的导数在任何地方都不为0。这对神经网络的学习非常重要。得益于这个斜率不会为0的性质,神经网络的学习得以正确进行。
梯度
梯度的计算
设
f
(
x
0
+
x
1
)
=
x
0
2
+
x
1
2
\begin{aligned}f(x_0+x_1)=x_0^2+x_1^2\end{aligned}
f(x0+x1)=x02+x12
def numerical_gradient(f, x):
h = 1e-4 # 0.0001
grad = np.zeros_like(x) # 生成和x形状相同的数组
for idx in range(x.size):
tmp_val = x[idx]
# f(x+h)的计算
x[idx] = tmp_val + h
fxh1 = f(x)
# f(x-h)的计算
x[idx] = tmp_val - h
fxh2 = f(x)
grad[idx] = (fxh1 - fxh2) / (2*h)
x[idx] = tmp_val # 还原值
return grad
求点(3_, 4)、 (0, 2)、 (3, _0)处的梯度。
**numerical_gradient(function_2, np.array([3.0, 4.0]))
**array([ 6., 8.])
**numerical_gradient(function_2, np.array([0.0, 2.0]))
**array([ 0., 4.])
**numerical_gradient(function_2, np.array([3.0, 0.0]))
**array([ 6., 0.])
梯度指示的方向是各点处的函数值减小最多的方向
机器学习的主要任务是在学习时寻找最优参数。同样,神经网络也必须在学习时找到最优参数(权重和偏置)。最优参数即损失函数取最小值时的参数。但是,一般而言,损失函数很复杂,参数空间庞大,我们不知道它在何处能取得最小值。而通过巧妙地使用梯度来寻找函数最小值(或者尽可能小的值)的方法就是梯度法。**梯度表示的是各点处的函数值减小最多的方向,但无法保证梯度所指的方向就是函数的最小值。 **
梯度法
在梯度法中,函数的取值从当前位置沿着梯度方向前进一定距离,然后在新的地方重新求梯度,再沿着新梯度方向前进,如此反复,不断地沿梯度方向前进,逐渐减小函数值的过程就是梯度法(gradient method)。梯度法是解决机器学习中最优化问题的常用方法,特别是在神经网络的学习中经常被使用。
严格地讲,寻找最小值的梯度法称为梯度下降法(gradient descent method),寻找最大值的梯度法称为梯度上升法(gradient ascent method)。但通过反转损失函数符号,求最小值或最大值的问题会变成相同的问题。一般来说,神经网络(深度学习)中,梯度法主要是指梯度下降法。
用数学式来表示梯度法,如式(4_.7)所示。
x
0
=
x
0
−
η
∂
f
∂
x
0
x
1
=
x
1
−
η
∂
f
∂
x
1
(
4.7
)
\begin{aligned}x_0&=x_0-\eta\frac{\partial f}{\partial x_0}\\x_1&=x_1-\eta\frac{\partial f}{\partial x_1}\end{aligned}\quad\quad\quad\quad\quad(4.7)
x0x1=x0−η∂x0∂f=x1−η∂x1∂f(4.7)
η_表示更新量,在神经网络的学习中,称为学习率(learningrate)。学习率决定在一次学习中,应该学习多少,以及在多大程度上更新参数。 式(4.7)是表示更新一次的式子,这个步骤会反复执行。也就是说,每一步都按式(4.7)更新变量的值,通过反复执行此步骤,逐渐减小函数值。
学习率需要事先确定为某个值,比如0.01或0.**001。一般而言,这个值过大或过小,都无法抵达一个“好的位置”。在神经网络的学习中,一般会一边改变学习率的值,一边确认学习是否正确进行了。 **
用Python来实现梯度下降法:
def gradient_descent(f, init_x, lr=0.01, step_num=100):
x = init_x
for i in range(step_num):
grad = numerical_gradient(f, x)
x -= lr * grad
return x
参数f是要进行最优化的函数,init_x是初始值,lr是学习率learningrate,step_num是梯度法的重复次数。numerical_gradient(f,x)会求函数的梯度,用该梯度乘以学习率得到的值进行更新操作,由step_num指定重复的次数。
神经网络的梯度
有一个只有一个形状为2 × 3的权重W的神经网络,损失函数用_L_表示。此时,梯度可以用 表示。用数学式表示的话,如下所示。
W
=
(
w
11
w
12
w
13
w
21
w
22
w
23
)
(
4.8
)
∂
L
∂
W
=
(
∂
L
∂
w
11
∂
L
∂
w
12
∂
L
∂
w
13
∂
L
∂
w
21
∂
L
∂
w
22
∂
L
∂
w
23
)
\begin{aligned} \boldsymbol{W}&& \left.=\left(\begin{array}{ccc}w_{11}&w_{12}&w_{13}\\w_{21}&w_{22}&w_{23}\end{array}\right.\right) \\ &&&&\left.\left(\begin{matrix}4.8\end{matrix}\right.\right) \\ \begin{aligned}\frac{\partial L}{\partial\boldsymbol{W}}\end{aligned}&& \left.=\left(\begin{array}{ccc}\frac{\partial L}{\partial w_{11}}&\frac{\partial L}{\partial w_{12}}&\frac{\partial L}{\partial w_{13}}\\\frac{\partial L}{\partial w_{21}}&\frac{\partial L}{\partial w_{22}}&\frac{\partial L}{\partial w_{23}}\end{array}\right.\right) \end{aligned}
W∂W∂L=(w11w21w12w22w13w23)=(∂w11∂L∂w21∂L∂w12∂L∂w22∂L∂w13∂L∂w23∂L)(4.8)
∂
L
∂
W
\frac{\partial L}{\partial W}
∂W∂L的元素由各个元素关于W的偏导数构成。比如,第1行第1列的元素
∂
L
∂
w
11
\frac{\partial L}{\partial w_{11}}
∂w11∂L表示当w11稍微变化时,损失函数L会发生多大变化。重点是
∂
L
∂
W
\frac{\partial L}{\partial W}
∂W∂L的形状和W相同。
学习算法的实现
神经网络的学习步骤:
前提
神经网络存在合适的权重和偏置,调整权重和偏置以便拟合训练数据的过程称为“学习”。神经网络的学习分成下面4个步骤。
步骤1( mini-batch)
从训练数据中随机选出一部分数据,这部分数据称为mini-batch。我们的目标是减小mini-batch的损失函数的值。
步骤2(计算梯度)
为了减小mini-batch的损失函数的值,需要求出各个权重参数的梯度。 梯度表示损失函数的值减小最多的方向。
步骤3(更新参数)
将权重参数沿梯度方向进行微小更新。
步骤4(重复)
重复步骤1、步骤2、步骤3。
因为使用的数据是随机选择的mini batch数据,所以又称为随机梯度下降法(stochastic gradient descent),一般由一个名为SGD的函数来实现,随机梯度下降法是“对随机选择的数据进行的梯度下降法”。
误差反向传播法
使用计算图来理解误差反向传播法
计算图
计算图的特征是可以通过传递“局部计算”获得最终结果。局部计算是指,无论全局发生了什么,都能只根据与自己相关的信息输出接下来的结果。
例如,假设(经过复杂的计算)购买的其他很多东西总共花费4000日元,各个节点处的计算都是局部计算,如苹果和其他很多东西的求和运算(4000 + 200 _→ _4200)并不关心4000是如何计算而来的,各个节点处只需进行与自己有关的计算,本例中只把两个数字相加即可。
为什么用计算图
- 局部计算
- 保存计算的中间结果
- 通过反向传播高效计算导数
购买2个苹果,每个100日元,消费税10%,最终支付220日元。假设我们想知道苹果价格的上涨会在多大程度上影响最终的支付金额,即求**“支付金额关于苹果的价格的导数”**。设苹果的价格为_x_,支付金额为_L_,则相当于求
∂
L
∂
x
\frac{\partial L}{\partial x}
∂x∂L。这个导数的值表示当苹果的价格稍微上涨时,支付金额会增加多少。
在图中,反向传播使用反向箭头(粗线)表示。反向传播传递“局部导数”,导数值在箭头的下方。
反向传播从右向左传递导数的值(1 _→ 1._1 _→ 2.2),可知,“支付金额关于苹果的价格的导数”的值是2.2,也就是苹果的价格上涨1日元,支付金额增加2.2日元(严格地讲,如果苹果的价格增加某个微小值,则最终的支付金额将增加那个微小值的2._2倍)。同理,“支付金额关于消费税的导数”、“支付金额关于苹果的个数的导数”都可以计算。并且,计算中途求得的导数的结果(中间传递的导数)可以被共享,从而可以高效地计算多个导数。
综上,计算图的优点是,可以通过正向传播和反向传播高效地计算各个变量的导数值。
链式法则
计算图的反向传播
反向传播的计算顺序是,将信号_E_乘以节点的局部导数(
∂
y
∂
x
\frac{\partial y}{\partial x}
∂x∂y),然后将结果传递给下一个节点。这里所说的局部导数是指正向传播中y = f(x)的导数,也就是_y_关于_x_的导数(
∂
y
∂
x
\frac{\partial y}{\partial x}
∂x∂y)。比如,假设y = f(x) = x2,则局部导数为
∂
y
∂
x
\frac{\partial y}{\partial x}
∂x∂y = 2_x_。把这个局部导数乘以上游传过来的值(本例中为_E_),然后传递给前面的节点。
如果某个函数由复合函数表示,则该复合函数的导数可以用构成复合函数的各个函数的导数的乘积表示。
z
=
t
2
t
=
x
+
y
(
5.1
)
\begin{aligned}z&=t^2\\t&=x+y\end{aligned}\quad\quad\quad\quad(5.1)
zt=t2=x+y(5.1)
链式求导为:
∂
z
∂
x
=
∂
z
∂
t
∂
t
∂
x
(
5.2
)
\begin{aligned}\frac{\partial z}{\partial x}=\frac{\partial z}{\partial t}\frac{\partial t}{\partial x}&\quad\quad\quad\quad(5.2)\end{aligned}
∂x∂z=∂t∂z∂x∂t(5.2)
偏导数为:
∂
z
∂
t
=
2
t
∂
t
∂
x
=
1
(
5.3
)
\begin{aligned}\frac{\partial z}{\partial t}&=2t\\\frac{\partial t}{\partial x}&=1\end{aligned}\quad\quad\quad\quad(5.3)
∂t∂z∂x∂t=2t=1(5.3)
则z关于x的偏导数即为:
∂
z
∂
x
=
∂
z
∂
t
∂
t
∂
x
=
2
t
⋅
1
=
2
(
x
+
y
)
(
5.4
)
\begin{aligned}\frac{\partial z}{\partial x}=\frac{\partial z}{\partial t}\frac{\partial t}{\partial x}=2t\cdot1=2(x+y)\quad\quad\quad\quad(5.4)\end{aligned}
∂x∂z=∂t∂z∂x∂t=2t⋅1=2(x+y)(5.4)
计算图为:
计算图的反向传播从右到左传播信号。反向传播的计算顺序是,先将节点的输入信号乘以节点的局部导数(偏导数),然后再传递给下一个节点。 反向传播时,“**2”节点的输入是
∂
z
∂
x
\frac{\partial z}{\partial x}
∂x∂z,将其乘以局部导数
∂
z
∂
t
\frac{\partial z}{\partial t}
∂t∂z(因为正向传播时输入是_t_、输出是_z_,所以这个节点的局部导数是 ),然后传递给下一个节点。另外,上图中反向传播最开始的信号
∂
z
∂
z
\frac{\partial z}{\partial z}
∂z∂z在前面的数学式中没有出现,这是因为
∂
z
∂
z
=
1
\frac{\partial z}{\partial z} = 1
∂z∂z=1,所以在刚才的式子中被省略了。
上图中需要注意的是最左边的反向传播的结果。根据链式法则,
∂
z
∂
z
∂
z
∂
t
∂
t
∂
x
=
∂
z
∂
t
∂
t
∂
x
=
∂
z
∂
x
\frac{\partial z}{\partial z}\frac{\partial z}{\partial t}\frac{\partial t}{\partial x} = \frac{\partial z}{\partial t}\frac{\partial t}{\partial x} = \frac{\partial z}{\partial x}
∂z∂z∂t∂z∂x∂t=∂t∂z∂x∂t=∂x∂z成立,对应“z_关于_x_的导数”。也就是说,反向传播是基于链式法则的。把式(5._3)的结果代入到上图中,结果如下图, 的结果为2(_x _+ y)。
")
反向传播
加法节点的反向传播
z
=
x
+
y
z=x+y
z=x+y,则
∂
z
∂
x
=
1
∂
z
∂
y
=
1
(
5.5
)
\begin{aligned}\frac{\partial z}{\partial x}&=1\\\frac{\partial z}{\partial y}&=1\end{aligned}\quad\quad\quad\quad(5.5)
∂x∂z∂y∂z=1=1(5.5)
计算图如下图表示:
加法节点的反向传播只乘以1,所以输入的值会原封不动地流向下一个节点。
乘法节点的反向传播
z
=
x
y
z = xy
z=xy,则
∂
z
∂
x
=
y
∂
z
∂
y
=
x
(
5.6
)
\begin{aligned}\frac{\partial z}{\partial x}&=y\\\frac{\partial z}{\partial y}&=x\end{aligned}\quad\quad\quad\quad(5.6)
∂x∂z∂y∂z=y=x(5.6)
计算图如下图表示:
乘法的反向传播会将上游的值乘以正向传播时的输入信号的“翻转值”后传递给下游。翻转值表示一种翻转关系,如图5-12所示,正向传播时信号是_x_的话,反向传播时则是_y_;正向传播时信号是_y_的话,反向传播时则是_x_。
回到买苹果
苹果的价格的导数是2_.2,苹果的个数的导数是110,消费税的导数是200。这可以解释为,如果消费税和苹果的价格增加相同的值,则消费税将对最终价格产生200倍大小的影响,苹果的价格将产生2._2倍大小的影响。不过,因为这个例子中消费税和苹果的价格的量纲不同,所以才形成了这样的结果(消费税的1是100%,苹果的价格的1是1日元)。
误差反向传播法的实现
前提
神经网络中有合适的权重和偏置,调整权重和偏置以便拟合训练数据的
过程称为学习。神经网络的学习分为下面4个步骤。
步骤1( mini-batch)
从训练数据中随机选择一部分数据。
步骤2(计算梯度)
计算损失函数关于各个权重参数的梯度。
步骤3(更新参数)
将权重参数沿梯度方向进行微小的更新。
步骤4(重复)
重复步骤1、步骤2、步骤3。
与学习相关的技巧
参数的更新
以下方法各有千秋,SGD相对较慢,其他三种使用较多,最近很多人大多喜欢使用Adam
SGD与SGD的缺点
W
←
W
−
η
∂
L
∂
W
(
6.1
)
\begin{aligned}\boldsymbol{W}\leftarrow\boldsymbol{W}-\boldsymbol{\eta}\frac{\partial L}{\partial\boldsymbol{W}}\quad\quad\quad\quad\quad(6.1)\end{aligned}
W←W−η∂W∂L(6.1)
SGD(stochastic gradient descent),随机梯度下降法,比起胡乱地搜索参数空间,也算是“聪明”的方法。
对于
f
(
x
,
y
)
=
1
20
x
2
+
y
2
(
6.2
)
\begin{aligned}f(x,y)=\frac{1}{20}x^2+y^2\quad\quad\quad\quad(6.2)\end{aligned}
f(x,y)=201x2+y2(6.2)
的图形(左图)和它的等高线(右图)“)
的梯度”)
假设从(x, y) = (_-7.0, 2._0)处(初始值)开始搜索,结果如下图所示。SGD呈“之”字形移动,所以,如果函数的形状非均向(anisotropic),搜索的路径就会非常低效。因此,我们需要比单纯朝梯度方向前进的SGD更聪明的方法。** SGD低效的根本原因是,梯度的方向并没有指向最小值的方向。 **
移动,效率低")
Momentum
v
←
α
v
−
η
∂
L
∂
W
(
6.3
)
W
←
W
+
v
(
6.4
)
\begin{aligned}v&\leftarrow\alpha v-\eta\frac{\partial L}{\partial\boldsymbol{W}}\quad\quad\quad\quad(6.3)\\\\\boldsymbol{W}&\leftarrow \boldsymbol{W}+v\quad\quad\quad\quad\quad\quad(6.4)\end{aligned}
vW←αv−η∂W∂L(6.3)←W+v(6.4)
v,对应物理上的速度。
下图中,更新路径就像小球在碗中滚动一样。这是因为虽然_x_轴方向上受到的力非常小,但是一直在同一方向上受力,所以朝同一个方向会有一定的加速。反过来,虽然_y_轴方向上受到的力很大,但是因为交互地受到正方向和反方向的力,它们会互相抵消,所以_y_轴方向上的速度不稳定。因此,和SGD时的情形相比,可以更快地朝_x_轴方向靠近,减弱“之”字形的变动程度。
AdaGrad
在关于学习率的有效技巧中,有一种被称为学习率衰减(learning ratedecay)的方法,即随着学习的进行,使学习率逐渐减小。也就是一开始“多”学,然后逐渐“少”学。
公式表示为:
h
←
h
+
∂
L
∂
W
⊙
∂
L
∂
W
(
6.5
)
W
←
W
−
η
1
h
∂
L
∂
W
(
6.6
)
\begin{aligned}&\boldsymbol{h}\leftarrow\boldsymbol{h}+\frac{\partial L}{\partial\boldsymbol{W}}\odot\frac{\partial L}{\partial\boldsymbol{W}}\quad\quad\quad\quad\quad\quad\quad(6.5)\\\\&\boldsymbol{W}\leftarrow\boldsymbol{W}-\eta\frac{1}{\sqrt{\boldsymbol{h}}}\frac{\partial L}{\partial\boldsymbol{W}}\quad\quad\quad\quad\quad\quad\quad(6.6)\end{aligned}
h←h+∂W∂L⊙∂W∂L(6.5)W←W−ηh1∂W∂L(6.6)
这里新出现了变量_h,它保存了以前的所有梯度值的平方和(式(6._5)中的
⊙
\odot
⊙表示对应矩阵元素的乘法)。然后,在更新参数时,通过乘以
1
h
\frac{1}{\sqrt{\boldsymbol{h}}}
h1,就可以调整学习的尺度。这意味着,参数的元素中变动较大(被大幅更新)的元素的学习率将变小。也就是说,可以按参数的元素进行学习率衰减,使变动大的参数的学习率逐渐减小。
AdaGrad会记录过去所有梯度的平方和。因此,学习越深入,更新的幅度就越小。实际上,如果无止境地学习,更新量就会变为 0,完全不再更新。为了改善这个问题,可以使用 RMSProp方法。RMSProp方法并不是将过去所有的梯度一视同仁地相加,而是逐渐地遗忘过去的梯度,在做加法运算时将新梯度的信息更多地反映出来。这种操作从专业上讲,称为“指数移动平均”,呈指数函数式地减小过去的梯度的尺度。
Adam
融合了Momentum和AdaGrad的方法。通过组合前面两个方法的优点,有望实现参数空间的高效搜索。基于Adam的更新过程就像小球在碗中滚动一样。虽然Momentun也有类似的移动,但是相比之下, Adam的小球左右摇晃的程度有所减轻。这得益于学习的更新程度被适当地调整了。
Adam会设置3个超参数。一个是学习率(论文中以_α_出现),另外两个是一次momentum系数_β_1和二次momentum系数_β_2。根据论文,标准的设定值是_β_1为0.9, _β_2 为0.999。设置了这些值后,大多数情况下都能顺利运行。
权重的初始值
权值衰减(weight decay)是抑制过拟合、提高泛化能力的技巧。是一种以减小权重参数的值为目的进行学习的方法。通过减小权重参数的值来抑制过拟合的发生。
todo: 看不懂
Batch Normalization
如果设定了合适的权重初始值,则各层的激活值分布会有适当的广度,从而可以顺利地进行学习。那么,为了使各层拥有适当的广度,“强制性”地调整激活值的分布会怎样呢?实际上, Batch Normalization方法就是基于这个想法而产生的。
优点:
• 可以使学习快速进行(可以增大学习率)。
• 不那么依赖初始值(对于初始值不用那么神经质)。
• 抑制过拟合(降低Dropout等的必要性)。
正则化
过拟合指的是只能拟合训练数据,但不能很好地拟合不包含在训练数据中的其他数据的状态。机器学习的目标是提高泛化能力,即便是没有包含在训练数据里的未观测数据,也希望模型可以进行正确的识别。我们可以制作复杂的、表现力强的模型,但是相应地,抑制过拟合的技巧也很重要。
发生过拟合的原因,主要原因为:1、模型拥有大量参数、表现力强;2、训练数据少。
权值衰减是一直以来经常被使用的一种抑制过拟合的方法。该方法通过在学习的过程中对大的权重进行惩罚,来抑制过拟合。很多过拟合原本就是因为权重参数取值过大才发生的。
当网络的模型变得很复杂时,就是用Dropout方法,Dropout方法在学习的过程中随机删除神经元,训练时,随机选出隐藏层的神经元,然后将其删除,被删除的神经元不再进行信号的传递。训练时,每传递一次数据,就会随机选择要删除的神经元。测试时,虽然会传递所有的神经元信号,但是对于各个神经元的输出,要乘上训练时的删除比例后再输出。
超参数的验证
超参数是指,比如各层的神经元数量、 batch大小、参数更新时的学习率或权值衰减等。如果这些超参数没有设置合适的值,模型的性能就会很差。虽然超参数的取值非常重要,但是在决定超参数的过程中一般会伴随很多的试错。下面介绍尽可能高效地寻找超参数的值的方法。
验证数据
调整超参数时,必须使用超参数专用的确认数据。用于调整超参数的数据,一般称为验证数据(validation data)。我们使用这个验证数据来评估超参数的好坏。
超参数的最优化
进行超参数的最优化时,逐渐缩小超参数的“好值”的存在范围非常重要。所谓逐渐缩小范围,是指一开始先大致设定一个范围,从这个范围中随机选出一个超参数(采样),用这个采样到的值进行识别精度的评估;然后,多次重复该操作,观察识别精度的结果,根据这个结果缩小超参数的“好值”的范围。通过重复这一操作,就可以逐渐确定超参数的合适范围。
超参数的范围只要“大致地指定”即可,也就是指像0_._001到1000这样,以“10的阶乘”的尺度指定范围。
超参数的最优化中,要注意的是深度学习需要很长时间(比如,几天或几周)。因此,在超参数的搜索中,需要尽早放弃那些不符合逻辑的超参数。于是,在超参数的最优化中,减少学习的epoch,缩短一次评估所需的时间是一个不错的办法。
超参数的最优化的步骤:
步骤0
设定超参数的范围。
步骤1
从设定的超参数范围中随机采样。
步骤2
使用步骤1中采样到的超参数的值进行学习,通过验证数据评估识别精度(但是要将epoch设置得很小)。
步骤3
重复步骤1和步骤2(100次等),根据它们的识别精度的结果,缩小超参数的范围
卷积神经网络
卷积神经网络(Convolutional Neural Network, CNN)。CNN被用于图像识别、语音识别等各种场合,在图像识别的比赛中,基于深度学习的方法几乎都以CNN为基础。本章将详细介绍CNN的结构,并用Python实现其处理内容。CNN中新出现了卷积层(Convolution层)和池化层(Pooling层)。
Affine层后面跟着激活函数ReLU层(或者Sigmoid层)。这里堆叠了4层“Affine-ReLU”组合,然后第5层是Affine层,最后由Softmax层输出最终结果(概率)。
CNN 中 新 增 了 Convolution 层 和 Pooling 层。 CNN 的层的连接顺序是“Convolution - ReLU -(Pooling)”(Pooling层有时会被省略)。这可以理解为之前的“Affi ne - ReLU”连接被替换成了“Convolution - ReLU -(Pooling)”连接。
卷积层
全连接的神经网络中使用了全连接层(Affine层)。在全连接层中,相邻层的神经元全部连接在一起,输出的数量可以任意决定。
全连接层的问题是数据的形状被“忽视”了。比如,输入一个1通道、高28像素、长28像素的(1_, 28, _28)形状的图像时,全连接层需要将3维数据拉平为1维数据。
图像是3维形状,这个形状中应该含有重要的空间信息。比如,空间上邻近的像素为相似的值、 RBG的各个通道之间分别有密切的关联性、相距较远的像素之间没有什么关联等, 3维形状中可能隐藏有值得提取的本质模式。但是,因为全连接层会忽视形状,将全部的输入数据作为相同的神经元(同一维度的神经元)处理,所以无法利用与形状相关的信息。
卷积层可以保持形状不变。当输入数据是图像时,卷积层会以3维数据的形式接收输入数据,并同样以3维数据的形式输出至下一层。因此,在CNN中,可以(有可能)正确理解图像等具有形状的数据。
另外, CNN 中,有时将卷积层的输入输出数据称为特征图(featuremap)。其中,卷积层的输入数据称为输入特征图(input feature map),输出数据称为输出特征图(output feature map)。
卷积运算
包含偏置的卷积运算的处理流程:
填充
在进行卷积层的处理之前,有时要向输入数据的周围填入固定的数据(比如0等),这称为填充(padding)。
步幅
应用滤波器的位置间隔称为步幅(stride)。之前的例子中步幅都是1,如果将步幅设为2,则如下图所示,应用滤波器的窗口的间隔变为2个元素。
3维数据的卷积运算
通道方向上有多个特征图时,会按通道进行输入数据和滤波器的卷积运算,并将结果相加,从而得到输出。多维卷积运算中,输入数据和滤波器的通道数要设为相同的值。
结合方块思考
批处理
池化层
池化是缩小高、长方向上的空间的运算。
池化层的特征:
没有要学习的参数
池化层和卷积层不同,没有要学习的参数。池化只是从目标区域中取最大值(或者平均值),所以不存在要学习的参数。
通道数不发生变化
经过池化运算,输入数据和输出数据的通道数不会发生变化。如图7-15所示,计算是按通道独立进行的。
对微小的位置变化具有鲁棒性(健壮)
输入数据发生微小偏差时,池化仍会返回相同的结果。因此,池化对输入数据的微小偏差具有鲁棒性。比如, 3 _× _3的池化的情况下,如图7-16所示,池化会吸收输入数据的偏差(根据数据的不同,结果有可能不一致)。
im2col(image to column)
im2col会把输入数据展开以适合滤波器(权重)。
为了便于观察,将步幅设置得很大,以使滤波器的应用区域不重叠。而在实际的卷积运算中,滤波器的应用区域几乎都是重叠的。在滤波器的应用区域重叠的情况下,使用im2col展开后,展开后的元素个数会多于原方块的元素个数。因此,使用im2col的实现存在比普通的实现消耗更多内存的缺点。但是,汇总成一个大的矩阵进行计算,对计算机的计算颇有益处。比如,在矩阵计算的库(线性代数库)等中,矩阵计算的实现已被高度最优化,可以高速地进行大矩阵的乘法运算。因此,通过归结到矩阵计算上,可以有效地利用线性代数库。