3.1从感知机到神经网络
- 神经网络的例子
第0层------输入层
第2层------中间层(隐藏层)
第3层------输出层
(以图1为例)

图1 2层神经网络
- 复习感知机

图2感知机

图3 表示出偏置(偏置的输入信号一直是1)

式1 感知机的数学式
为了简化上式,引入sigmoid,将式1改写成式2

式2 引入新函数h(x),超过0输出1,否则输出0

式3 h(x)
- 激活函数
节点a:信号的总加权和
节点y:节点a被激活函数h(x)转换成了节点y
(注:"节点"与"神经元"含义相同)
与一般的神经元图相比,图4在神经元内部明确显示出来激活函数的计算过程
补充:鱼书中的**"朴素感知机"** 是单层 网络,指的是激活函数使用了阶跃函数;
"多层感知机" 是指神经网络 ,使用sigmoid 函数等平滑的激活函数的多层网络。

图4 激活函数的计算过程
3.2激活函数
- sigmoid函数
神经网络中用sigmoid函数作为激活函数,进行信号的转换,转换后的信号被传送到下一个神经元。
这也是感知机和神经网络的主要区别(什么区别呢?感知机一般都用阶跃函数?

式4 sigmoid 函数
- 阶跃函数的实现
NumPy实现阶跃函数
import numpy as np
import matplotlib.pyplot as plt
def step_function(x):
return np.array(x>0,dtype=int) #x>0,如果元素>0,结果为true;np.array将布尔数组转换成整数数组
x=np.arange(-5.0,5.0,0.1)
y=step_function(x) #出现了x and y must have same first dimension的报错,发现时没有写(x)
print("x.shape:", x.shape)
print("y.shape:", y.shape)
#绘图
plt.plot(x,y)
plt.ylim(-0.1,1.1)
plt.show()

图4 阶跃函数图像
-
sigmoid函数的实现
import numpy as np
import matplotlib.pyplot as plt#sigmoid函数
x=np.arange(-5.0,5.0,0.1)
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)
plt.show()
图5 sigmoid函数
- sigmoid函数&阶跃函数的比较

图6 阶跃&sigmoid
阶跃 :感知机中神经元之间流动的是二元信号(0或1)
sigmoid :神经网络中流动的是连续的实数值信号
共同点:输入小时,输出接近0;输入大时,输出靠近1(当输入信号为重要信息时,二者都会输出较大的值。
- 非线性函数
神经网络的激活函数必须使用非线性函数。
若使用线性函数,则永远存在(等效的)"无中间层的神经网络"
例如,选择线性函数h(x)=ax,那么将h(x)作为激活函数,3层神经网络对应的运算y=h(h(h(x)))可等价为y=a*x(a=c^3),即:无中间层的神经网络
- ReLU函数

式5 ReLU函数公式

图7 ReLU函数图像
摸鱼看到了一个回答,将上述内容解释得很清楚:
编辑神经网络为什么可以(理论上)拟合任何函数?701 赞同 · 42 评论回答
3.3 多维数组的运算
- 3.3.1多维数组

图1 一维数组
import numpy as np
A=np.array([1,2,3,4])
print(A)
A.shape

图2 多维数组
B=np.array([[1,2],[3,4]])
B.shape
- 3.3.2矩阵乘法

式1 矩阵(二维数组)乘积公式
2*2 的矩阵A和2*2的矩阵B的乘积计算
A=np.array([[1,2],[3,4]])
A.shape
B=np.array([[5,6],[7,8]])
B.shape
np.dot(A,B)

图3 代码运行结果图
3x2的A矩阵和2x3的B矩阵乘积
!A矩阵的列数==B矩阵的行数
A=np.array([[1,2,3],[3,4,5]])
A.shape
B=np.array([[5,6],[7,8],[1,3]])
B.shape
np.dot(A,B)

图4 3x2的矩阵和2x3的矩阵乘积代码运行结果
- 3.3.3神经网络的内积
忽略偏置b和激活函数,只考虑权重w的神经网络

图5 矩阵的乘积进行神经网络的计算
x=np.array([1,2])
x.shape
w=np.array([[1,3,5],[2,4,6]])
w.shape
y=np.dot(x,w)
print(y)

图6 代码实现神经网络的计算
3.4 3层神经网络的实现
输入层(第0层):2个神经元
第1个隐藏层(第1层):3个神经元
第2个隐藏层(第2层):2个神经元
输出层(第3层):2个神经元

图1 3层神经网络示意图
- 3.4.1符号确认

图2 权重的符号表示
- 3.4.2 各层间信号传递的实现
输入层到第1层的第1个神经元的信号传递

图 3 输入层到第1层的信号传递
图3对应的第1层第1个神经元的计算公式:

式1 第1层第1个神经元的计算公式
使用矩阵的乘法运算,第1层的加权和计算公式为:

式2 第1层的加权和计算公式(使用了矩阵的乘法计算)
用NumPy多维数组来实现以上计算过程

图4 将式2用代码实现并得到结果
引入激活函数sigmoid函数进行处理

图5 在图4的基础上进行了激活,得到上图结果
第1层到第2层的传递
示意图

图6 第1层到第2层的信号传递示意图
代码实现
(疑惑点:为什么权重w2是3x2的矩阵呢?其行数与Z的列数需相等,所以行数是3我可以理解,但是为什么列数是3呢?(这个问题暂时搁置,先容我想想
补充:下面代码中的Z是第1层神经元,所以就相当于输入层到第1层的代码实现中的x

图 7 第1层到第2层的代码实现
第2层到输出层的传递
传递分析图

图8 第2层到输出层的信号传递图
代码实现
输出层的激活函数不是sigmoid函数,可以视为没有激活函数,只是原样输出,只是为了形式上的统一
疑惑:(为什么输出层的激活函数不用sigmoid函数,而是用恒等函数呢

图9 第2层到输出层的代码实现
输出层的激活函数(一般情况下):
回归问题:使用恒等函数
二元分类问题:使用sigmoid函数
多元分类问题:使用softmax函数
(在3.5 输出层的设计中会详细解释)
- 3.4.3 代码实现小结
代码实现小结略
以上都是输入到输出的传递处理,即前向(forward) ,与之对应的有输出到输入的传递处理,即后向(backward)
3.5 输出层的设计
- 3.5.1 恒等函数(sigma)和softmax函数
恒等函数:将输入原样输出

图1 恒等函数的转换处理 图示(一根箭头)
softmax函数

图2 softmax函数
softmax计算公式

式1 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

图3 计算结果
- 3.5.2 实现softmax函数的注意事项
softmax函数中有指数运算,容易出现超大值之间的除法运算,以及会出现超大值无法表示的情况,称之为溢出
溢出问题改进:
公式推导:

式2 softmax函数改进公式
理解:上述公式中的C`可以取-max( 𝑎𝑖 ),避免分子分母出现超大值
溢出问题解决示例:

图4 a=[1000,1010,1020]时结果无法显示
引入常数c,避免分子分母出现超大值,解决了溢出问题

图5 溢出问题解决
- 3.5.3 softmax函数的特征
softmax函数的输出总和为1,因此,softmax函数的输出解释为"概率"
代码解释:
#3.5.3 softmax函数的特征
import numpy as np
a=np.array([0.13,2.9,4.0])
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.13,2.9,4.0])
print(softmax(a))
out=np.sum(softmax(a))
print(out)
运行结果:

图6 softmax函数输出总和为1的·验证
由于softmax函数是单调递增函数,因此,输入的大小值和输出的大小值保持一致。由于一般情况下,神经网络只把输出值最大的神经元对应的类别作为识别结果,由于使用softmax函数,输出值最大的神经元的位置也不会变。因此,神经网络在进行分类时,输出层的softmax函数可以省略。
- 3.5.4 输出层的神经元数量
输出层的神经元数量需要根据待解决的问题来决定,对于分类问题,输出层的神经元数量一般设定为类别的数量。
例如手写数字识别的输出层神经元数量是10
3.6 手写数字识别
使用神经网络解决问题与求解机器学习的过程类似,分别是学习+推理
- 3.6.1 MNIST数据集
MNIST数据集是由0到9的数字图像构成的,分为训练图像 和测试图像
学习 :先使用训练图像进行学习
训练 :将学习到的模型来对测试图像进行分类
# coding: utf-8
import sys, os
sys.path.append(os.pardir) #从父目录导入文件的设置
import numpy as np
from dataset.mnist import load_mnist #!导入MNIST数据集的加载函数
from PIL import Image #导入图像处理库,用来显示图像
def img_show(img):
pil_img = Image.fromarray(np.uint8(img)) #将Nnumpy数组转换为PIL
pil_img.show()
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=False, normalize=False)
'''flatten=True 将28x28的二维矩阵转换为784的一维数组,设置为F,即保持原图;
normalize=False 设置为T,即将像素0-255 缩放到0-1'''
#取了第一张照片
img = x_train[0]
label = t_train[0]
print(label) # 5
print(img.shape) # (784,)
img = img.reshape(28, 28)
'''将形状转换为原始图像大小28x28'''
print(img.shape) # (28, 28)
img_show(img)

图1 代码运行结果

图2 img_show(x_train[0]) --reshape之后的
关于上面代码的思考:
- 为什么要flatten (二维→一维)再reshape(一维→二维)呢?
传统神经网络(非卷积网络)只能处理一维
2.normalize=False
此处没有进行归一化(将像素值从0-255除以255,变成0.0~1.0的小数)
归一化:让数据范围一致,加速模型训练
3.reshape理解
reshape只是改变了数组的view,在内存中的线性存储顺序不变,只是按新形状重新划分行列
dsR1讲解chat.deepseek.com/a/chat/s/61bd12f3-073f-4317-8530-92ee32bedd73
- 3.6.2 神经网络的推理处理
神经网络神经元剖析:
输入层:784个神经元(28x28)
第1个隐藏层:50个神经元
第2个隐藏层:100个神经元
输出层:10个神经元(对应10个不同的数字)
预处理
对神经网络的输入数据进行某种既定的转换
在这个例子中,对输入图像进行了正规化的预处理(将每个像素值除以255,压缩至0.0~1.0的范围之间)
定义函数
get_data(),init_network(),predict()
# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 親ディレクトリのファイルをインポートするための設定
import numpy as np
import pickle
from dataset.mnist import load_mnist
from common.functions import sigmoid, softmax
def get_data():
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, flatten=True, one_hot_label=False)
return x_test, t_test
def init_network():
with open("sample_weight.pkl", 'rb') as f:
network = pickle.load(f)
return network
def predict(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 = softmax(a3)
return y
x, t = get_data()
network = init_network()
accuracy_cnt = 0
for i in range(len(x)):
y = predict(network, x[i])
p= np.argmax(y) # 获取概率最高的元素的索引
if p == t[i]:
accuracy_cnt += 1
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))
输出结果:

图3 93.52%的数据被正确分类
- 3.6.2 批处理
输出神经网络的各层权重的形状

图4 各层权重的形状

图5 数组形状的变化
将多张图像进行打包(用predict函数打包处理100张图像)
将X的形状改为100x784即可

图6 批处理中数组形状的变化
- 3.7 小结
- 神经网络中的激活函数常用sigmoid或ReLU函数
- 使用NumPy多维数组,可以高效实现神经网络
- 机器学习可大致分为分类问题,回归问题
- 输出层的激活函数,回归问题中常用恒等函数,分类问题中常用softmax函数
- 分类问题中,输出层的神经元数量设置为要分类的类别数
- 输入数据的集合称为批,通过以批为单位进行推理处理,能够实现高速的运算
注意:离线环境运行neuralnet_mnist.py提示不能下载,可以先将训练数据图片下载后放到书本源码dataset目录中
参考:《深度学习入门:基于python的理论和实现》