参考书籍:《深度学习入门 : 基于Python的理论与实现 》/ (日) 斋藤康毅著 ; 陆宇杰译. – 北京 : 人民邮电出版社, 2018.7(2019.5重印)、
文章中带编号的图片(如:图3-14)均来自书籍内容。
使用NumPy矩阵实现神经网络需要注意输入值 x x x和权重 w w w的维数需要对应,其中:
x x x:存储输入信号的一维数组(向量)
w
w
w:存储权重参数的多维数组(矩阵)
输入到输出的前向传播:
联系激活函数的内容:
激活函数:
a
=
b
+
w
1
x
1
+
w
2
x
2
(3.4)
a = b + w_1x_1 + w_2x_2 \tag{3.4}
a=b+w1x1+w2x2(3.4)
y = h ( a ) (3.5) y=h(a) \tag{3.5} y=h(a)(3.5)
第一层的神经元
a
1
(
1
)
a_1^{(1)}
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)} \tag{3.8}
a1(1)=w11(1)x1+w12(1)x2+b1(1)(3.8)
使用矩阵的乘法运算,将第一层的加权和表示成:
A
(
1
)
=
X
W
(
1
)
+
B
(
1
)
(3.9)
A^{(1)}=XW^{(1)}+B^{(1)} \tag{3.9}
A(1)=XW(1)+B(1)(3.9)
A
(
1
)
=
(
a
1
(
1
)
,
a
2
(
1
)
,
a
3
(
1
)
)
A^{(1)}=(a_1^{(1)},a_2^{(1)},a_3^{(1)})
A(1)=(a1(1),a2(1),a3(1))
X = ( x 1 , x 2 ) X=(x_1,x_2) X=(x1,x2)
B ( 1 ) = ( b 1 ( 1 ) , b 2 ( 1 ) , b 3 ( 1 ) ) B^{(1)}=(b_1^{(1)},b_2^{(1)},b_3^{(1)}) B(1)=(b1(1),b2(1),b3(1))
W
(
1
)
=
[
w
11
(
1
)
w
21
(
1
)
w
31
(
1
)
w
12
(
1
)
w
22
(
1
)
w
32
(
1
)
]
W^{(1)} = \left[ {
其实就是线型代数中方程组的矩阵表示
代码实现3层神经网络:
import numpy as np
def sigmoid_function(x):
"""sigmoid激活函数"""
return 1 / (1 + np.exp(-x))
def identity_function(x):
"""恒等函数"""
return x
# 输入信号,x1 = 1.0, x2 = 0.5
X = np.array([1.0, 0.5])
"""第一层的前向传播"""
# 第一层的权重参数W1
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
# 第一层的偏置参数B1
B1 = np.array([0.1, 0.2, 0.3])
# 计算a
A1 = np.dot(X, W1) + B1
# 激活函数
Z1 = sigmoid_function(A1)
"""第二层的前向传播"""
W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid_function(A2)
"""第三层的前向传播,输出层"""
W3 = np.array([[0.1, 0.3], [0.2, 0.4]])
B3 = np.array([0.1, 0.2])
A3 = np.dot(Z2, W3) + B3
Z3 = identity_function(A3) # 输出层所用的激活函数,此处为恒等函数
print(Z3) # [0.31682708 0.69627909]
神经网络比较标准的代码格式,由书中作者提供:
import numpy as np
def sigmoid_function(x):
"""sigmoid激活函数"""
return 1 / (1 + np.exp(-x))
def identity_function(x):
"""恒等函数"""
return x
def init_network():
"""进行权重和偏置的初始化"""
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_function(a1)
a2 = np.dot(z1, W2) + b2
z2 = sigmoid_function(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函数:
y
k
=
e
x
p
(
a
k
)
∑
i
=
1
n
e
x
p
(
a
i
)
(3.10)
y_k=\frac{exp(a_k)}{\sum_{i=1}^nexp(a_i)} \tag{3.10}
yk=∑i=1nexp(ai)exp(ak)(3.10)
e
x
p
(
x
)
exp(x)
exp(x)=
e
x
e^x
ex
y k y_k yk: 第k个神经元的输出 y k y_k yk
softmax函数的分子是输入信号 a k a_k ak的指数函数,分母是所有输入信号的指数函数的和。
softmax函数的代码实现:
import numpy as np
def softmax_function(x):
return np.exp(x) / np.sum(np.exp(x))
a = np.array([0.3, 2.9, 4.0])
y = softmax_function(a)
print(y) # [0.01821127 0.24519181 0.73659691]
softmax函数的实现中要进行指数函数的运算,但是指数函数的值很容易变得非常大,要注意溢出问题。
改进函数:
改进函数代码实现:
import numpy as np
def softmax(x):
"""防止溢出的方法"""
c = np.max(x)
return np.exp(x - c) / np.sum(np.exp(x - c))
a = np.array([1010, 1000, 990])
y = softmax(a)
print(y) # [9.99954600e-01 4.53978686e-05 2.06106005e-09]
print(np.sum(y)) # 1.0
softmax函数的性质:
因为性质2,所以可以将函数的输出解释为“概率”
import numpy as np
def softmax(x):
c = np.max(x)
return np.exp(x - c) / np.sum(np.exp(x - c))
a = np.array([0.3, 2.9, 4.0])
y = softmax(a)
print(y) # [0.01821127 0.24519181 0.73659691]
print(np.sum(y)) # 1.0
y中的值视为概率,可以看到最后一个元素的“概率”最高,所以答案就是第三个类别
一般神经网络只把输出值最大的神经元所对应的类别作为识别结果
机器学习问题的步骤为:学习和推理
在推理阶段用学到的模型对未知的数据进行推理,因为softmax函数的性质3,各个元素的大小关系不变,所以在推理时一般会省略输出层的softmax函数
对于分类问题,输出层的神经元数量一般设定为类别的数量
比如手写数字识别,有0到9一共十个数字,所以输出层的神经元数量设定为10.