初学,若有错误,恳请指正。
目录
初学,若有错误,恳请指正。
3.1 从感知机到神经网络
3.1.1 神经网络的例子
3.1.2 复习感知机
3.1.3 激活函数登场
3.2 激活函数
3.2.1 sigmoid 函数
3.2.2 阶跃函数的实现
3.2.3 阶跃函数的图形
3.2.4 sigmoid 函数的实现
3.2.5 sigmoid 函数和阶跃函数的比较
3.2.6 非线性函数
3.2.7 ReLU 函数
3.3 多维数组的运算
3.3.1 多维数组
3.3.2 矩阵乘法
3.3.3 神经网络的内积
3.4 3层神经网络的实现
3.4.1 符号确认
3.4.2 各层间信号传递的实现
3.5 输出层的设计
3.5.1 恒等函数和 softmax 函数
3.5.2 实现 softmax 函数时的注意事项
3.5.3 softmax 函数的特征
3.5.4 输出层的神经元数量
3.6 手写数字识别
3.6.1 MNIST 数据集
3.6.2 神经网络的推理处理
3.6.3 批处理
3.7 小结
3.1 从感知机到神经网络
3.1.1 神经网络的例子
在神经元的连接上面,神经网络和感知机是一样的:
3.1.2 复习感知机
前面我们说过,感知机可以将输入的多个信号再加入各自的权重之后输出为一个信号。主要的概念有偏置b,权重w。
如何根据输入信号转换出输出信号呢?这里就需要提到激活函数。
3.1.3 激活函数登场
既然叫激活函数,那么我们就可以将上述感知机的表达式转换为和函数相关的式子。
从上面的3.1式可以看出来,输出为0还是1是通过这一串来决定的,那么我们把他整体设为x,然后引入一个h(x)
让他转换为输出y,这里的h(x)就被称为是激活函数。
3.2 激活函数
输入和输出是有一定的映射关系,激活函数就是将神经元输入映射到输出。
3.2.1 sigmoid 函数
其中exp(-x) 表示求e的-x次方。e 是纳皮尔常数 2.7182 . . .
3.2.2 阶跃函数的实现
阶跃函数就是当阈值超过某个值时,改变输出的函数。例如前面说的感知机,在不超过阈值的时候为0,超过阈值之后输出为1,这就是一种阶跃函数的体现。
根据上述例子,写一串代码来实现阶跃函数:
def step_function(x):
if x > 0:
return 1
else:
return 0
上述是最简单的方式,但是他只能处理单个值,无法处理NumPy 数组。要想处理NumPy 数组,就需要考虑到我们学过的一些小细节,在numpy数组和0比较之后,输出的结果会是布尔型,但是我们需要的结果是数值型,所以需要引进astype()方法来实现数据类型的转化,例如:
def step_function(x):
y=x>0
return y.astype(np.int)
这样就把返回值转换成了整型,以方便后续的调用。
3.2.3 阶跃函数的图形
根据上面的理解,写代码来实现阶跃函数:
import numpy as np
import matplotlib.pyplot as plt
def step_function(x):
y = x > 0
return y.astype(int) #np.int在较高版本的numpy中已经废弃了,所以直接使用int
x = np.arange(-5.0,5.0,0.1)
y = step_function(x)
plt.plot(x,y)
plt.ylim(-0.1,1.1)
plt.show()
这里的阶跃函数中的两句话也可以直接合并:
return np.array(x>0,type(int))
表示将输入的数组x中的元素与0进行比较,大于0为True,否则为False,然后将布尔类型转换为整型。
结果图如下:
3.2.4 sigmoid 函数的实现
原理上只是表达式不同,所以直接修改表达式:
import numpy as np
import matplotlib.pyplot 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)
plt.show()
结果图如下:
3.2.5 sigmoid 函数和阶跃函数的比较
可以从图像中看出,阶跃函数是在到达一个值后直接跳转,但是sigmoid函数是一个平滑的曲线,也就是说,sigmoid函数还可以得到的0.几这样的结果。
这两者也有共同点,就是在输入比较小时,输出越接近0;输入大时,输出越接近1。也就是说,当输入信号为重要时, 它们都会输出较大的值;当输入信号为不重要的信息时, 两者都输出较小的值。还有一个共同点是,不管输入信号有多小,或者有多大,输出信号的值都在 0 到 1 之间。
3.2.6 非线性函数
前面我们提到过,线性函数是一条直线,从图像上可以看出,上面我们提到的两个激活函数均为非线性函数。
除此之外,前面我们也说过,多层感知机是需要通过非线性函数来形成的,所以可以说,激活函数是为了引进非线性属性,激活函数是连接单层感知机和神经网络的桥梁。
3.2.7 ReLU 函数
relu函数是当输入大于0时,输出该数,否则输出0。表达式如下:
所以要实现relu函数就要比较,输入数和0的大小,输出较大的值,使用maximum()函数:
import numpy as np
import matplotlib.pyplot as plt
def relu(x):
return np.maximum(0,x)
x = np.arange(-5.0,5.0,0.1)
y = relu(x)
plt.plot(x,y)
plt.ylim(-1,5.5)
plt.show()
结果如下:
3.3 多维数组的运算
3.3.1 多维数组
数组的维度从0开始,0维可以认为是标量,然后就是一维(向量)、二维(矩阵)、三维(及以上称为多维)。
数组的维度可以使用np.ndim()函数得知,数组的形状使用shape()方法得知。
3.3.2 矩阵乘法
两个矩阵相乘的前后顺序也是有要求的,是第一个矩阵的某行元素乘以第二个数组的某列元素,然后各个元素相乘相加得到结果的某个值。
这就要求第一个元素的列数等于第二个元素的行数,否则会出错。
矩阵的乘法使用np.dot()函数实现。
可以从上图得知,结果的行数和列数是和第一个矩阵的行数和第二个矩阵的列数相同的。
3.3.3 神经网络的内积
直接将输入和权重设置成矩阵,那么神经网络的内积就可以直接按照上面矩阵的乘法进行运算了。
3.4 3层神经网络的实现
3.4.1 符号确认
输入信号的符号不变,权重的符号重点关注下标,是后一个神经元的标号前一个神经元的标号,如上图。
3.4.2 各层间信号传递的实现
根据表达式,可以求出a:
那么将输入,权重等设置成矩阵,得到如下的表达式:
上面的这个疑问是我刚看的时候提出的,但是看3.9表达式就可以得到解答,X是两行一列的一个矩阵,所以要想权重矩阵和它能够相乘,就需要有两列,所以就得到了上面的W的矩阵。
知道了上述的计算方式之后,就按照三层神经网络的结构进行写代码,使用的激活函数是sigmoid函数,最后一层使用恒等函数(就是不变直接输出,所以没必要另外写这个函数)
要实现这个神经网络,首先需要定义使用的参数,大写的字母表示矩阵,那么就需要设置X,W1,W2,W3,B,A1,Z1(Z表示通过激活函数转换的结果),A2,Z2,A3。将这些矩阵初始化之后存到字典当中,方便后续的使用。
然后再定义一个函数,实现正向传播的过程。
得到的代码如下:
import numpy as np
def sigmoid(x):
return 1/(1+np.exp(-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
return a3
x = np.array([1.0,0.5])
network = init_network()
y = forward(network,x)
print(y)
3.5 输出层的设计
3.5.1 恒等函数和 softmax 函数
一般恒等函数用于回归模型(通过输入预测输出),softmax用于分类(将数据分成几种)。
softmax函数表达式如下:
分子是求输入信号k的指数,分母是求所有输入信号的指数和。
import numpy as np
def softmax(x):
a = np.exp(x)
b = np.sum(a) #这里输入的也是个数组,所以直接求和就可以
return a/b
x = np.array([0.3, 2.9, 4.0])
print(softmax(x))
结果:
3.5.2 实现 softmax 函数时的注意事项
可能会出现溢出问题(计算机处理“数”时,数值必须在 4 字节或 8 字节的有限数据宽度内。 这意味着数存在有效位数,也就是说,可以表示的数值范围是有 限的。因此,会出现超大值无法表示的问题。这个问题称为溢出, 在进行计算机的运算时必须(常常)注意。),所以一般都是在求指数前减去数组中的最大值,然后再计算。
import numpy as np
def softmax(x):
c = np.max(x)
a = np.exp(x-c)
b = np.sum(a)
return a/b
x = np.array([0.3, 2.9, 4.0])
print(softmax(x))
输出结果不变,原因如下:
3.5.3 softmax 函数的特征
输出总和为 1。
import numpy as np
def softmax(x):
c = np.max(x)
a = np.exp(x-c)
b = np.sum(a)
return a/b
x = np.array([0.3, 2.9, 4.0])
y = softmax(x)
print(y)
print(np.sum(y))
正因为有了这个性质,我们才可以把 softmax 函数的输出解释为“概率”。
神经网络只把输出值最大的神经元所对应的类别作为识别结果。
3.5.4 输出层的神经元数量
输出层的神经元数量需要根据待解决的问题来决定。比如,如果要将一批数据分为0……9这10类,那么输出层的神经元数量就是10。
输出值最大的即为网络预测的类别。
3.6 手写数字识别
3.6.1 MNIST 数据集
MNIST 数据集是由 0 到 9 的数字图像构成的。MNIST 的图像数据是 28 像素 × 28 像素的灰度图像(1通道),各个像素的取值在 0 到 255 之间。每个图像数据都相应地标有“7”“2”“1”等标签。
首先就是数据集的读入,使用的是load_mnist()函数。
这里就不运行了,我就先学习里面的一些基本概念以及表示了:
MNIST 数据读入:
from dataset.mnist import load_mnist
#表示从 dataset 包中的 mnist 子模块导入 load_mnist 函数,用于加载 MNIST 数据集。
load_mnist 函数以“( 训练图像 , 训练标签 ),( 测试图像,测试标签 )”的 形式返回读入的 MNIST 数据,例如:
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=True, normalize=False,one_hot_label=False)
fatten表示是否将输入数组展平为一维数组。
normalize表示是否将输入图像正规化为 0.0~1.0 的值。(将各个像素值除以 255,是简单的正规化)
one_hot_label表示是否将标签设置为0,1这样。若为False,则直接按照图像中的数值设置,比如图像中为7,则标签为7;若为True,则仅正确解标签为 1,错误的为0。
显示 MNIST 图像:
reshape() 方法的参数指定期望的形状,更改 NumPy 数组的形状。
把保存为 NumPy 数组的图像数据转换为 PIL 用的数据对象,这个转换处理由 Image.fromarray() 来完成。
3.6.2 神经网络的推理处理
init_network() 会读入保存在 pickle 文件 sample_weight.pkl 中的学习到的权重参数 A。这个文件中以字典变量的形式保存了权重和偏置参数。(应该就是和前面我们写的那个network那样)
识别精度:能在多大程度上正确分类
首先获得 MNIST 数据集,生成网络。接着,用 for 语句逐一取出保存在 x 中的图像数据,用 predict() 函数进行分类。predict() 函数以 NumPy 数组的形式输出各个标签对应的概率。然后,我们取出这个概率列表中的最大值的索引(第几个元素的概率最高),作为预测结果。可以用 np.argmax(x) 函数取出数组中的最大值的索引,np.argmax(x) 将获取被赋给参数 x 的数组中的最大值元素的索引。最后,比较神经网络所预测的答案和正确解标签,将回答正确的概率作为识别精度。
3.6.3 批处理
若同时处理100张,就可以称为批处理(batch_size)
实现代码如下:
axis=1表示在每一组y_batch中分别找出值最大的索引,例如:
每一组中最大的分别为0.8,0.6,0.5,0.8,那么索引就是1,2,1,0(索引从0开始)。
accuracy_cnt += np.sum(p == t[i:i+batch_size])则表示找出i到i+batch_size中标签正确的总数。
3.7 小结
神经网络中使用的是平滑变化的 sigmoid函数,而感知机中使用的是信号急剧变化的阶跃函数。
因为时间的原因,没有自己把手写数字识别打一遍,只是大致的过了一遍,后面有时间的话就自己试一遍,没时间就先这样吧,明天学习第四章,神经网络的学习。