深度学习入门基于Python的理论与实现——神经网络(二)

发布于:2022-10-20 ⋅ 阅读:(357) ⋅ 点赞:(0)

多维数组的运算

多维数组

简单地讲,多维数组就是“数字的集合”,数字排成一列的集合、排成长方形的集合、排成三维状或者(更加一般化的)N维状的集合都称为多维数组。


>>> import numpy as np
>>> A = np.array([1, 2, 3, 4])
>>> print(A)
[1 2 3 4]
>>> np.ndim(A) # 数组的维数可以通过np.ndim()函数获得
1
>>> A.shape # 数组的形状可以通过实例变量shape获得
(4,)
>>> A.shape[0]
4

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

>>> B = np.array([[1,2], [3,4], [5,6]]) # 生成了一个3 × 2的数组B
>>> print(B) # 3 × 2的数组表示第一个维度有3个元素,第二个维度有2个元素
[[1 2]
 [3 4]
 [5 6]]
>>> np.ndim(B)
2
>>> B.shape
(3, 2)

第一个维度对应第0维,第二个维度对应第1维(Python的索引从0开始)。二维数组也称为矩阵。
数组的横向排列称为行,纵向排列称为列。
在这里插入图片描述

矩阵乘法

在这里插入图片描述
矩阵的乘积是通过左边矩阵的行(横向)和右边矩阵的列(纵
向)以对应元素的方式相乘后再求和而得到的。并且,运算的结果保存为新的多维数组的元素。

如图,A的第1行和B的第1列的乘积结果是新数组的第1行第1列的元素,A的第2行和B的第1列的结果是新数组的第2行第1列的元素。

计算2 × 2形状的矩阵的乘积的例子

>>> A = np.array([[1,2], [3,4]])
>>> A.shape
(2, 2)
>>> B = np.array([[5,6], [7,8]])
>>> B.shape
(2, 2)
>>> np.dot(A, B) # 计算它们的乘积算(点积)
array([[19, 22],
	  [43, 50]])

注意:np.dot(A, B)和np.dot(B, A)的值可能不一样。和一般的运算(+或*等)不同,矩阵的乘积运算中,操作数(A、B)的顺序不同,结果也会不同。

计算2 × 3的矩阵和3 × 2 的矩阵的乘积的例子

>>> A = np.array([[1,2,3], [4,5,6]])
>>> A.shape
(2, 3)
>>> B = np.array([[1,2], [3,4], [5,6]])
>>> B.shape
(3, 2)
>>> np.dot(A, B)
array([[22, 28],
	  [49, 64]])

矩阵A的形状是2 × 3,矩阵B的形状是3 × 2,矩阵A的第1维的元素个数(3)和矩阵B的第0维的元素个数(3)相等。如果这两个值不相等,则无法计算矩阵的乘积。

>>> C = np.array([[1,2], [3,4]])
>>> C.shape
(2, 2)
>>> A.shape
(2, 3)
>>> np.dot(A, C)
Traceback (most recent call last):
 File "<stdin>", line 1, in <module>
ValueError: shapes (2,3) and (2,2) not aligned: 3 (dim 1) != 2 (dim 0)

注意:在多维数组的乘积运算中,必须使两个矩阵中的对应维度的元素个数一致,否则就会报错。

在这里插入图片描述
当A是二维矩阵、B是一维数组时,如图3-13所示,对应维度
的元素个数要保持一致的原则依然成立。
在这里插入图片描述

神经网络的内积

使用NumPy矩阵来实现神经网络。我们以图中的简单神经网络为对象。这个神经网络省略了偏置和激活函数,只有权重。
在这里插入图片描述

3层神经网络的实现

现在我们来进行神经网络的实现。这里我们以图的3层神经网络为
对象,实现从输入到输出的(前向)处理。在代码实现方面,使用上一节介绍的NumPy多维数组。巧妙地使用NumPy数组,可以用很少的代码完成神经网络的前向处理。
在这里插入图片描述

3层神经网络:输入层(第0层)有2个神经元,第1个隐藏层(第1层)有3个神经元,第2个隐藏层(第2层)有2个神经元,输出层(第3层)有2个神经元

符号确认

如图所示,权重和隐藏层的神经元的右上角有一个“(1)”,它表示
权重和神经元的层号(即第1层的权重、第1层的神经元)。此外,权重的右下角有两个数字,它们是后一层的神经元和前一层的神经元的索引号。
在这里插入图片描述

各层间信号传递的实现

在这里插入图片描述
图中增加了表示偏置的神经元“1”。请注意,偏置的右下角的索引
号只有一个。这是因为前一层的偏置神经元(神经元“1”)只有一个。

任何前一层的偏置神经元“1”都只有一个。偏置权重的数量取决于后一层的神经元的数量(不包括后一层的偏置神经元“1”)

现在用数学式表示通过加权信号和偏置的和进行计算
在这里插入图片描述
如果使用矩阵的乘法运算,则可以将第1层的加权和表示成下面
的数学式
在这里插入图片描述
在这里插入图片描述
接下来我们用NumPy多维数组来实现数学式,这里将输入信号、权重、偏置设置成任意值。

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

我们观察第1层中激活函数的计算过程,如下图,隐藏层的加权和(加权信号和偏置的总和)用a表示,被激活函数转换后的信号用z表示。图中h()表示激活函数,这里我们使用的是sigmoid函数(在神经网络(一)中定义的sigmoid()函数)。
从输入层到第1层的信号传递

Z1 = sigmoid(A1)
print(A1) # [0.3, 0.7, 1.1]
print(Z1) # [0.57444252, 0.66818777, 0.75026011]

在这里插入图片描述
第1层到第2层的信号传递

W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])
print(Z1.shape) # (3,)
print(W2.shape) # (3, 2)
print(B2.shape) # (2,)
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)

从第2层到输出层的信号传递

def identity_function(x):
 return x
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

这里是引用我们定义了identity_function()函数(也称为“恒等函数”),并将其作为输出层的激活函数。
输出层所用的激活函数,要根据求解问题的性质决定。一般地,回
归问题可以使用恒等函数,二元分类问题可以使用 sigmoid函数,多元分类问题可以使用 softmax函数。

代码实现小结

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]

init_network()函数会进行权重和偏置的初始化,并将它们保存在字典变量network中。这个字典变量network中保存了每一层所需的参数(权重和偏置)。forward()函数中则封装了将输入信号转换为输出信号的处理过程。

到这里神经网络的前向处理的实现就完成了。通过巧妙地使用NumPy多维数组,我们高效地实现了神经网络。


网站公告

今日签到

点亮在社区的每一天
去签到