介绍完神经网络的结构之后,现在我们来试着解决实际问题。这里我们来进行手写数字图像的分类。假设学习已经全部结束,我们使用学习到的参数,先实现神经网络的“推理处理”。这个推理处理也称为神经网络的前向传播(forward propagation)。
运行书籍源代码文件
首先先下载书籍的源代码文件
点击链接深度学习入门:基于Python的理论与实现 (ituring.com.cn)
下载完成之后解压全部文件
之后自动跳转到文件目录。
然后我们打开pycharm,新建一个项目,注意配置虚拟环境。
如何在pycharm配置虚拟环境
01创建新的项目
在Pycharm中创建新的project,
interpreter type 选择custon environment
environment选择generate new。
type就用Virtualenv。
base Python就用默认的。
02在这个项目中创建新的Python文件
注意不要在.venv中
03使用terminal工具在虚拟环境中安装包。
03.1
先在file setting tool 中找到terminal,shell path 选择cmd(Windows系统)
03.2
在虚拟环境中安装包。(快捷键Alt+F12使用terminal)
pip install *
04记录一下下载了什么包
pip freeze > requirement.txt
(虽然你只想下了一个包,但是这个包可能又用了其他包,于是你就下了不止一个)
配置好了虚拟环境,我们将三个文件复制到我们刚刚新建的项目中。一个是ch03,一个是dataset,一个是commom。
然后在pycharm里面,点开Terminal下载包,下载numpy和pillow。
打开文件ch03,打开mnist_show.py
即可运行成功,如图
以上就是成功书籍源代码来学习的展示。接下来进行 进行书籍部分源代码的详细解释
代码解释
mnist_show.py
ch03中的mnist_show.py的代码如下
这第二句就看不懂 ,sys.path.append(os.pardir)是什么意思咧?我们先看看sys和os是什么。
1.sys
sys是python的一个模块
- 模块搜索路径:
sys.path
是一个列表,包含了 Python 解释器搜索模块的路径。可以通过修改这个列表来添加自定义的模块搜索路径。
2.os
os是python的一个模块,可以与操作系统交互,可以实现文件与目录的功能。
os.listdir(path)可以列出指定路径下的文件和目录
os.mkdir(path)创建文件夹
os.removedir(path)删除文件夹
os.path.exsits(path)检查文件夹是否存在
os.pardir
是一个字符串常量,表示当前目录的父目录
由源代码可知,mnist_show.py在ch03这个目录下,而load_mnist函数在dataset.mnist文件夹目录下的mnist_py文件中。mnist_show.py不能跨目录导入这个函数,故加了这条语句。sys.path.append(os.path)其实是将括号内的文件添加到python模块搜索目录中,而os.path就是指的当前目录的父目录。
3.load_mnist函数的三个参数
load_mnist函数在mnist.py文件中定义的。
normalize:将图像像素值正规化
one_hot_label:是否使用one-hot数组来作为标签
flatten:是否将图像展开为一维数组
将normalize设置成True后,函数内部会进行转换,将图像的各个像素值除以255,使得数据的值在0.0~1.0的范围内。像这样把数据限定到某个范围内的处理称为正规化(normalization)。此外,对神经网络的输入数据进行某种既定的转换称为预处理(pre-processing)。这里,作为对输入图像的一种预处理,我们进行了正规化
neuralnet_mnist_batch.py
下面,我们对这个MNIST数据集实现神经网络的推理处理。神经网络的输入层有784个神经元,输出层有10个神经元。输入层的784这个数字来源于图像大小的28×28 =784,输出层的10这个数字来源于10类别分类(数字0到9,共10类别)。此外,这个神经网络有2个隐藏层,第1个隐藏层有50个神经元,第2个隐藏层有100个神经元。这个50和100可以设置为任何值。下面我们先定义get_data()、init_network()、predict()这3个函数(代码在ch03/neuralnet_mnist.py中)。
几个函数的定义
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
定义get_data函数,在函数中又调用了load_mnist函数,使用正规化,将图像展开为一维数组,不使用one_hot_label标签。
返回测试输入数据和测试标签数据。
def init_network():
with open("sample_weight.pkl", 'rb') as f:
network = pickle.load(f)
return network
定义init_network函数,使用with 语句打开文件(可以读取数据后及时关闭),'rb'表示用二进制只读模式打开数据。
init_network()会读入保存在pickle文件sample_weight.pkl中的学习到的权重参数。这个文件中以字典变量的形式保存了权重和偏置参数。
network = pickle.load(f)
:使用pickle
模块的load
函数从打开的文件对象f
中加载数据,并将加载的数据赋值给变量network
。pickle
模块常用于序列化和反序列化 Python 对象。
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
定义predict函数
这个函数包含了神经网络的计算过程,其中运用了矩阵点乘的运算,sigmoid函数作为隐藏层的激活函数,softmax函数作为输出层的激活函数。
整个函数的功能就是输入x和权重w,用神经网络的计算方式来计算y,并输出y。
调用函数开始计算
x, t = get_data()
network = init_network()
batch_size = 100 # 批数量
accuracy_cnt = 0
第一句调用函数,将函数的返回值赋值给x和t
batch_size表示批数量为100。
for i in range(0, len(x), batch_size):
x_batch = x[i:i+batch_size]
y_batch = predict(network, x_batch)
p = np.argmax(y_batch, axis=1)
accuracy_cnt += np.sum(p == t[i:i+batch_size])
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))
x[i:i+batch_size]
x使测试的输入数据
i使一个索引变量
x[i:i+batch_size]这是切片切片操作,这里batch_size的具体数值是100,表示选取x数据的第i个到i+99个,并将这些数据赋值给x_batch。而这里还有一个循环操作
range(0, len(x), batch_size):range用于生成整数序列,从0开始,到len(x)结束,步长为batch_size。这意味着循环变量i依次取0,1*batch,2*batch...
直到接近但不超过len(x)
的值。
所以这里就是一个将x数据分成很多批的操作,每一批的数据为100。
y_batch这一句用到了predict的函数,将函数计算出的y,赋值给变量y_baych。
y_batch是一个二维数组,形状是(num_samples,num_classes),其中num_samples表示样本数量,num_classes表示类别数量,如果 y_batch
是一个形状为 (5, 3)
的数组,代表有 5 个样本,每个样本有 3 个类别得分,可能如下所示
y_batch = [[0.2, 0.6, 0.2],
[0.3, 0.4, 0.3],
[0.1, 0.1, 0.8],
[0.4, 0.5, 0.1],
[0.25, 0.25, 0.5]]
argmax是numpy库的一个函数,是来找数据中的每一行或者每一列的最大,参数axis=1表示按行找最大值,参数axis=0表示按列找最大值。比如,这里我们要找每一行中三个类别的最大值,来看看哪个类别可能性最大。
import numpy as np
y_batch = [[0.2, 0.6, 0.2],
[0.3, 0.4, 0.3],
[0.1, 0.1, 0.8],
[0.4, 0.5, 0.1],
[0.25, 0.25, 0.5]]
p = np.argmax(y_batch, axis=1)
print(p)
#运行结果[1 1 2 1 2]
最后源代码的
accuracy_cnt += np.sum(p == t[i:i+batch_size])
这里就是求正确率.
p == t[i:i+batch_size]就是求数组p与标签数组中的数是否一致,返回的一个含布尔值的数组,其中每一个元素表示对应的预测结果是否与真实标签相等。
np.sum(p == t[i:i+batch_size]) 就是计算Ture的数量
accuracy_cnt += np.sum(p == t[i:i+batch_size]) 将预测正确的样本数量累计加在accuracy_cnt上,这样,在循环遍历所有批次的过程中,accuracy_cnt
会不断增加,最终得到总的预测正确的样本数量。
print("Accuracy:" + str(float(accuracy_cnt) / len(x)))
最后代码运行,将正确率 打印出来。
至此我们数字识别的代码实践就结束了,整个过程我们并没有进行机器学习,只是假设所有的参数已经找到,然后神经网络来进行推理的过程。
为什么使用批处理
批处理一次性计算大型数组要比分开逐步计算各个小型数组速度更快。
1.减少函数调用开销
2.高效利用数据计算的库的优化能力
3.充分利用GPU的并行性
神经网络的形状
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
不知道大家看到这个'W1','W2','W3'有没有疑惑。权重参数不是有很多吗,为什么只要三个?
其实W1,W2,W3就是数组,他们的形状分别是
我们前面提到了这个神经网络一共有输入层,输入层的元素是784个,两个隐藏层,输出层,输出层的元素是10个。 所以x.shape(10000,784)意思就是一共有10000个图片,每一个图片有784个像素,所以输入层需要784个神经元。有10000行,有784列。
W1是从输入层到第一层的权重。它的形状是(784,50),这里的784是它的每一列的元素个数,50是它的每一行的元素个数,大家还记得神经网络那一篇笔记的内容吗?
我们给每一给权重做了做了标记,它下角标第一个字母表示什么,第二个字母表示什么。
也许你不记得了,但是你记得矩阵的乘法,我们要用xW相乘,那么x的每一行的元素个数,要等于W的每一列的元素个数。
那么我希望大家心中有两个图,一个是矩阵的图,一个是神经网路的图。
这里我放一个简单的W,输入层有12个神经元,第一个隐藏层有3个神经元。W下角标第一个字母表示隐藏层的神经元的序号,第二个字母表示输入层的神经元的序号。
是的你会为这个标号感到奇怪,为什么和你学到的矩阵的下标方式不一样。
其实如果用Wx相乘就可以了,既满足神经网络的标号,又满足矩阵的标号,但是不满足numpy数组的行与列的表示,所以只能退而求其次了。
创作不易,如果这个对你有帮助欢迎点赞关注,我期待大家自己下载代码看看,实践一下。有问题欢迎评论区留言!