从输入到输出:探索三层神经网络的工作原理

本文介绍了一个具有两个隐藏层的三层神经网络的实现过程。首先,通过符号确认,明确了权重和偏置的表示方法。然后,详细介绍了各层间信号传递的实现,包括输入层到第一隐藏层、激活函数的计算过程、第一隐藏层到第二隐藏层以及第二隐藏层到输出层。其中,激活函数采用了sigmoid函数,并介绍了如何使用NumPy实现神经网络的矩阵运算。最后,通过整理代码,展示了神经网络的前向传播过程。这篇文章详细阐述了三层神经网络的实现细节,对于理解神经网络的工作原理具有很好的参考价值。

前言

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

在这里插入图片描述

一、符号确认

神经网络的运算可以通过矩阵运算来实现。神经网络的每一层运算都可以看作是通过矩阵乘法来实现的(从宏观角度看)

我们首先从定义符号开始。请看下图,突出显示了从输入层神经元 x 2 x_2 x2 到后一层的神经元的权重。

在这里插入图片描述

权重和隐藏层的神经元的右上角有一个“(1)”,表示权重和神经元的层号(即第1层的权重、第1层的神经元)。此外,权重的右下角有两个数字,它们分别是后一层的神经元和前一层的神经元的索引号。比如, w 12 ( 1 ) w_{12}^{(1)} w12(1) 表示前一层的第2个神经元 x 2 x_2 x2 到后一层的第1个神经元的权重。权重右下角按照“后一层的索引号、前一层的索引号”的顺序排列。

二、各层间信号传递的实现

输入层到第1层

从输入层到第1层的第1个神经元的信号传递过程

在这里插入图片描述

图中增加了表示偏置的神经元 “1”。

请注意, 偏置的右下角的索引号只有一个。这是因为前一层的偏置神经元 (神经元 “ 1 ”) 只有一个 1 { }^1 1

a 1 ( 1 ) a_1^{(1)} a1(1) 通过加权信号和偏置的和按如下方式进行计算:
a 1 ( 1 ) = w 11 ( 1 ) x 1 + w 12 ( 1 ) x 2 + b 1 ( 1 ) a_1^{(1)}=w_{11}^{(1)} x_1+w_{12}^{(1)} x_2+b_1^{(1)} a1(1)=w11(1)x1+w12(1)x2+b1(1)
第 1 层的加权和的矩阵乘法运算:
A ( 1 ) = X W ( 1 ) + B ( 1 ) \boldsymbol{A}^{(1)}=\boldsymbol{X} \boldsymbol{W}^{(1)}+\boldsymbol{B}^{(1)} A(1)=XW(1)+B(1)

其中, A ( 1 ) 、 X 、 B ( 1 ) 、 W ( 1 ) \boldsymbol{A}^{(1)} 、 \boldsymbol{X} 、 \boldsymbol{B}^{(1)} 、 \boldsymbol{W}^{(1)} A(1)XB(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} \boldsymbol{A}^{(1)} & =\left(\begin{array}{lll} a_1^{(1)} & a_2^{(1)} & a_3^{(1)} \end{array}\right), \boldsymbol{X}=\left(\begin{array}{ll} x_1 & x_2 \end{array}\right), \boldsymbol{B}^{(1)}=\left(\begin{array}{lll} b_1^{(1)} & b_2^{(1)} & b_3^{(1)} \end{array}\right) \\ \boldsymbol{W}^{(1)} & =\left(\begin{array}{lll} w_{11}^{(1)} & w_{21}^{(1)} & w_{31}^{(1)} \\ w_{12}^{(1)} & w_{22}^{(1)} & w_{32}^{(1)} \end{array}\right) \end{aligned} A(1)W(1)=(a1(1)a2(1)a3(1)),X=(x1x2),B(1)=(b1(1)b2(1)b3(1))=(w11(1)w12(1)w21(1)w22(1)w31(1)w32(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  # [0.3, 0.7, 1.1]

激活函数的计算过程

在这里插入图片描述

如图所示, 隐藏层的加权和 (加权信号和偏置的总和)用 a a a 表示, 被激活函数转换后的信号用 z z z 表示。此外, 图中 h ( ) h() h() 表示激活函数, 这里我们使用的是 sigmoid 函数。

import numpy as np

def sigmoid(x):
    return 1 / (1 + np.exp(-x))
Z1 = sigmoid(A1)
print(Z1) #[0.57444252, 0.66818777, 0.75026011]

sigmoid()函数接收NumPy数组, 并返回元素个数相同的NumPy数组。

第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)

除了第1层的输出(Z1)变成了第2层的输入这一点以外,这个实现和刚才的代码完全相同。

第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()函数(也称为“恒等函数”),并将其作为输出层的激活函数。恒等函数会将输入按原样输出,当然也没有必要特意定义identity_function()。这里只是为了和之前的流程保持统一。另外,输出层的激活函数用 σ ( ) σ() σ() 表示,不同于隐藏层的激活函数 h ( ) h() h() σ σ σ 读作sigma)。

激活函数的选择通常根据问题的性质来确定:

  • 对于回归问题,可以使用恒等函数,将神经网络的输出直接作为预测值。
  • 对于二元分类问题,可以使用Sigmoid函数,将输出压缩到0到1之间,表示概率。
  • 对于多元分类问题,可以使用Softmax函数,将输出转换为表示各个类别概率的向量。

三、代码整理

至此,我们已经介绍完了3层神经网络的实现。现在我们把之前的代码 实现全部整理一下。这里,我们按照神经网络的实现惯例,只把权重记为大 写字母W1,其他的(偏置或中间结果等)都用小写字母表示。

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()和forward()函数。init_network()函数会进行权重和偏置的初始化,并将它们保存在字典变量network中。这个字典变量network中保存了每一层所需的参数(权重和偏置)。

forward()函数中则封装了将输入信号转换为输出信号的处理过程。 forward(前向)表示的是从输入到输出方向 的传递处理。


在这里插入图片描述

  • 21
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的派大星星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值