python 底层实现_用Python从底层实现一个多层感知机
在上一篇文章中,我们从数学理论对多层感知机的反向传播进行了推导。南柯一梦宁沉沦:神经网络中反向传播算法数学推导zhuanlan.zhihu.com
这一篇文章中我们将基于上一篇文章最后给出的算法使用Python语言来实现一个多层感知机。
完整代码以及代码的使用方法,可以光顾我的GithubProfessorHuang/Python_LeNet_UnderlyingImplementationgithub.com
MNIST数据准备
要进行训练,我们第一步需要先准备好训练数据,在这里我们使用经典的MNIST数据集。MNIST数据集的获取有多种方式,存储的格式也各不相同。在这里,我建议直接在MNIST的官网,即Yann Lecun的个人网站上获取。http://yann.lecun.com/exdb/mnist/index.html
我们可以在网站上下载得到四个压缩包,分别是训练图片,训练标签,测试图片和测试标签,解压之后获得四个文件的存储格式为idx。我们需要使用Python的struct库中unpack函数进行解析。然后使用numpy库将数据转换成numpy数组的形式便于我们之后处理。
图片数据解析为一个三维数组(也可称为张量),格式为图片数量×784×1.需要提前将图片数据从0-255的整数转换成0-1的浮点数。标签数据解析为一维数据,只存储了标签值的一个标量。我们需要将它也转换成一个三维数组,格式为标签数量×10×1,每个标签以one-hot格式存储,即一个10维列向量,正确标签为下标的数为1,其它的为0.
import numpy as np
from struct import unpack
def read_image(path):
with open(path, 'rb') as f:
magic, num, rows, cols = unpack('>4I',f.read(16))
img = np.fromfile(f, dtype=np.uint8).reshape(num, 784, 1) # 在这里可以调整图片读入格式
return img
def read_label(path):
with open(path, 'rb') as f:
magic, num = unpack('>2I',f.read(8))
label = np.fromfile(f, dtype=np.uint8)
return label
def normalize_image(image):
img = image.astype(np.float32)/255.0
return img
def one_hot_label(label):
lab = np.zeros((label.size, 10))
for i, row in enumerate(lab):
row[label[i]] = 1
return lab
# 加载数据集以及数据预处理
def dataset_loader():
train_image = read_image(r'C:\Users\95410\Downloads\数据集\MNIST\train-images.idx3-ubyte')
train_label = read_label(r'C:\Users\95410\Downloads\数据集\MNIST\train-labels.idx1-ubyte')
test_image = read_image(r'C:\Users\95410\Downloads\数据集\MNIST\t10k-images.idx3-ubyte')
test_label = read_label(r'C:\Users\95410\Downloads\数据集\MNIST\t10k-labels.idx1-ubyte')
train_image = normalize_image(train_image)
train_label = one_hot_label(train_label)
train_label = train_label.reshape(train_label.shape[0],train_label.shape[1],1)
test_image = normalize_image(test_image)
test_label = one_hot_label(test_label)
test_label = test_label.reshape(test_label.shape[0],test_label.shape[1],1)
return train_image, train_label, test_image, test_label
train_image, train_label, test_image, test_label = dataset_loader()
train_image维度为60000×784×1,train_label维度为60000×10×1
test_image维度为10000×784×1,test_label维度为10000×10×1
编写神经网络类
初始化神经网络
我们定义一个神经网络类NetWork,将涉及到的函数与神经网络各层参数封装在里面。
class NetWork(object):
def __init__(self, sizes):
'''初始化神经网络,给每层的权重和偏置赋初值权重为一个列表,列表中每个值是一个二维n×m的numpy数组偏置为一个列表,列表中每个值是一个二维n×1的numpy数组'''
self.num_layers = len(sizes)
self.sizes = sizes
self.weights = [np.random.randn(n,m) for m,n in zip(sizes[:-1], sizes[1:])] # 一定得用rnadn而不是random
self.biases = [np.random.randn(n,1) for n in sizes[1:]]
在初始化一个神经网络时,我们只需要以列表的形式提供神经网络各层的大小,神经网络各层的权重矩阵以及偏置项便会随机初始化并以二维numpy数组的进行存储,并以列表的形式按顺序存放在self.weights以及self.biases中,可以让该神经网络中其它方法使用与更新。
注意权重矩阵的初始化使用np.random.randn提供标准正态分布的随机数,它有正也有负。而我之前不小心使用了np.random.random提供的是0-1的浮点数,结果导致神经网络无法训练。
神经网络前向传播
神经网络前向传播很简单,取出权重矩阵和偏置项,通过矩阵乘法运算和矩阵相加运算,再经过激活函数即可根据前一层的输出得到当前层的输出,递归运算下去即可得到神经网络最终的输出。
def feed_forward(self, x):
'''完成前向传播过程,由输入值计算神经网络最终的输出值输入为一个列向量,输出也为一个列向量'''
value = x
for i in range(len(self.weights)):
value = self.sigmoid(np.dot(self.weights[i], value) + self.biases[i])
y = value
return y
矩阵的乘法用np.dot函数实现。我们使用的是sigmoid函数
作为激活函数。
def sigmoid(self, z):
'''sigmoid激活函数'''
a = 1.0 / (1.0 + np.exp(-z))
return a
神经网络反向传播
根据输入列向量x,前向传播出各层激活前的输出
和激活后的输出
为了之后计算delta误差以及损失函数对权重矩阵的导数。
将最后一层输出
与标签列向量y代入到损失函数对
的导数,求得最后一层的delta误差
。
利用公式
可以依次求出每层的delta误差,Hadmard积直接用*符号即可,表示逐元素相乘。
每求出一层的delta误差,便可以很快的带入公式,该层的偏置的导数与delta误差相等,该层权重矩阵的导数等于该层delta误差右乘上上一层激活后输出的转置。
def backprop(self, x, y):
'''计算通过单幅图像求得的每层权重和偏置的导数'''
delta_nabla_b = [np.zeros(b.shape) for b in self.biases]
delta_nabla_w = [np.zeros(w.shape) for w in self.weights]
# 前向传播,计算各层的激活前的输出值以及激活之后的输出值,为下一步反向传播计算作准备
activations = [x]
zs = []
for b, w in zip(self.biases, self.weights):
z = np.dot(w, activations[-1]) + b
zs.append(z)
activation = self.sigmoid(z)
activations.append(activation)
# 先求最后一层的delta误差以及b和W的导数
cost = activations[-1] - y
delta = cost * self.sigmoid_prime(zs[-1])
delta_nabla_b[-1] = delta
delta_nabla_w[-1] = np.dot(delta, activations[-2].transpose())
# 将delta误差反向传播以及各层b和W的导数,一直计算到第二层
for l in range(2, self.num_layers):
delta = np.dot(self.weights[-l+1].transpose(), delta) * self.sigmoid_prime(zs[-l])
delta_nabla_b[-l] = delta
delta_nabla_w[-l] = np.dot(delta, activations[-l-1].transpose())
return delta_nabla_b, delta_nabla_w
backprop方法只是根据一副图像以及对应的标签求得神经网络参数的导数,而我们使用随机梯度下降法,需要使用一个batch的数据来更新数据。sigmoid_prime是对sigmoid函数的一阶导数:
def sigmoid_prime(self, z):
'''sigmoid函数的一阶导数'''
return self.sigmoid(z) * (1 - self.sigmoid(z))
我们使用方法update_mini _batch来调用backprop方法实现对一个batch的数据进行更新
def update_mini_batch(self, mini_batch_image, mini_batch_label, eta, mini_batch_size):
'''通过一个batch的数据对神经网络参数进行更新需要对当前batch中每张图片调用backprop函数将误差反向传播求每张图片对应的权重梯度以及偏置梯度,最后进行平均使用梯度下降法更新参数'''
nabla_b = [np.zeros(b.shape) for b in self.biases]
nabla_w = [np.zeros(w.shape) for w in self.weights]
for x,y in zip(mini_batch_image, mini_batch_label):
delta_nabla_b, delta_nabla_w = self.backprop(x, y)
nabla_b = [nb+dnb for nb, dnb in zip(nabla_b, delta_nabla_b)]
nabla_w = [nw+dnw for nw, dnw in zip(nabla_w, delta_nabla_w)]
self.weights = [w-(eta/mini_batch_size)*nw for w, nw in zip(self.weights, nabla_w)]
self.biases = [b-(eta/mini_batch_size)*nb for b, nb in zip(self.biases, nabla_b)]
update_mini_batch方法一次接收一个batch的训练图片和对应的训练标签,根据该batch数据求得的参数的平均导数,使用梯度下降法对神经网络中各层权重矩阵与偏置进行更新。
我们需要使用整个60000张训练数据来对神经网络进行训练,因此我们需要一个更高层的函数SGD,接收训练数据,并将训练数据分成一个个batch,再调用update_mini_batch方法对参数进行更新。
def SGD(self, train_image, train_label, epochs, mini_batch_size, eta):
'''Stochastic gradiend descent随机梯度下降法,将训练数据分多个batch一次使用一个mini_batch_size的数据,调用update_mini_batch函数更新参数'''
for j in range(epochs):
mini_batches_image = [train_image[k:k+mini_batch_size] for k in range(0, len(train_image), mini_batch_size)]
mini_batches_label = [train_label[k:k+mini_batch_size] for k in range(0, len(train_label), mini_batch_size)]
for mini_batch_image, mini_batch_label in zip(mini_batches_image, mini_batches_label):
self.update_mini_batch(mini_batch_image, mini_batch_label, eta, mini_batch_size)
print("Epoch{0}: accuracy is{1}/{2}".format(j+1, self.evaluate(test_image, test_label), len(test_image)))
SGD接收的参数中,epochs代表训练轮数,将60000张数据全部训练一遍称为一个epoch。mini_batch_size表示batch大小,即一次使用多少张图片对参数进行更新。eta表示学习率。
验证神经网络准确率
我们在SGD方法中可以看见evaluate方法,它在每训练完一个epoch数据后,使用10000张测试数据来验证我们神经网络的准确率。
def evaluate(self, images, labels):
result = 0
for img, lab in zip(images, labels):
predict_label = self.feed_forward(img)
if np.argmax(predict_label) == np.argmax(lab):
result += 1
return result
验证的方法很简单,依次从验证数据集中取出图片,经过神经网络前向传播,看最终预测值与图片的标签是否一致即可。evaluate方法返回10000张图片中预测正确的数量。
测试我们的神经网络
我们使用两行代码即可对我们的神经网络定义以及训练
# 训练神经网络
net_trained = NetWork([784, 30, 10])
net_trained.SGD(train_image, train_label, 30, 10, 3)
我们设置一个三层的神经网络,唯一的一个隐藏层只有30个神经元,可以加快我们的训练速度。我们调用SGD方法,训练30个epoch,batch大小为10,学习率为3.这些参数都是我们可以调整的,但相应地会取得不同的训练效果,不合适的参数有时候会导致训练无法正确进行。
完成30个epoch的训练,在我的电脑上大概只需要3分钟即可,而我们神经网络对MNIST验证集的预测正确率已经可以达到94.85%。bingo!
参考:
[1]刘建平Pinard:深度神经网络(DNN)反向传播算法(BP)深度神经网络(DNN)反向传播算法(BP) - 刘建平Pinard - 博客园www.cnblogs.com
[2] Neural Networks and Deep Learning by By Michael NielsenNeural networks and deep learningneuralnetworksanddeeplearning.com
[3] 孤独暗星: MNIST手写数字数据集的读取,基于python3https://blog.csdn.net/weixin_40522523/article/details/82823812blog.csdn.net
python 底层实现_用Python从底层实现一个多层感知机相关推荐
- python 概率分布模型_使用python的概率模型进行公司估值
python 概率分布模型 Note from Towards Data Science's editors: While we allow independent authors to publis ...
- python 时间序列预测_使用Python进行动手时间序列预测
python 时间序列预测 Time series analysis is the endeavor of extracting meaningful summary and statistical ...
- python 字节流分段_由Python历史「解密」Python底层逻辑
一次纯粹的hacking Python的作者,Guido von Rossum,荷兰人.1982年,Guido从阿姆斯特丹大学获得了数学和计算机硕士学位.尽管,他算得上是一位数学家,但他更加享受计算机 ...
- python底层与机器底层关系_由Python历史「解密」Python底层逻辑
一次纯粹的hacking Python的作者,Guido von Rossum,荷兰人.1982年,Guido从阿姆斯特丹大学获得了数学和计算机硕士学位.尽管,他算得上是一位数学家,但他更加享受计算机 ...
- python queue 调试_学Python不是盲目的,是有做过功课认真去了解的
有多少伙伴是因为一句'人生苦短,我用Python'萌生想法学Python的!我跟大家更新过很多Python学习教程普及过多次的Python相关知识,不过大家还是还得计划一下Python学习路线!Pyt ...
- python医学图像读取_对python读取CT医学图像的实例详解
需要安装OpenCV和SimpleItk. SimpleItk比较简单,直接pip install SimpleItk即可. 代码如下: #coding:utf-8 import SimpleITK ...
- python精通时间_学Python需要多久能学会?精通Python需要多长时间?
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 严格意思上的说,Python其实是一个脚本语言,编程语言多种多样,但是却有开源和闭源之分,Python就是一种开放核心源代码编程语言,其开发代码的效率非常 ...
- python笛卡尔_用Python 3来模拟笛卡尔积
在数学中,两个集合 和 的笛卡尔积,是所有可能的有序对组成的集合,其中有序对的第一个对象是 的成员,第二个对象是 的成员.在集合论中表示为 ,例子如下: . 例如,集合 , ,那么这两个集合的笛卡尔积 ...
- python移动图形工作站_让Python跑得更快
原标题:让Python跑得更快 点击关注 异步图书,置顶公众号 每天与你分享 IT好书 技术干货 职场知识 Tips 参与文末话题讨论,即有机会获得异步图书一本. Python很容易学.你之所以阅读本 ...
最新文章
- Storm 01之 Storm基本概念及第一个demo
- Eslint配置文件 `.eslintrc.js`
- python能做表格吗-python可以用来做excel吗
- Windows Mobile 开发系列文章收藏 - 讨论篇
- 【学术相关】你只看到了200万年薪的招聘,看不到被困校园的几十万博士
- 老李分享:Android -自动化埋点 2
- Ruby:字符集和编码学习总结
- 解决HbuiderX将uni-app开发的项目运行到小程序编译后文件vendor.js太大的问题
- 如何使用explain进行SQL语句调优
- MYSQL优化派生表(子查询)在From语句中的
- Asp.Net母版页和内容页运行机制
- linux系统 看com口,Linux如何设置com1口,让超级终端通过com1口进行登录
- 管家婆 源码 php,在windows平台上构建本身的PHP
- Linux中eclipse配置Maven,eclipse maven选项怎么配置settings
- php fseek函数,php fseek函数怎么用
- 电容或电感的电压_电容与电感的对偶性小结
- 翻转数组,将数组倒序输出
- css vw vh ie9,css3中calc、vw、vh、vmin、vmax 属性的应用及兼容性详解
- canvas绘制圆形头像
- android实现qq邮箱多个图标效果