700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > python神经网络案例——FC全连接神经网络实现mnist手写体识别

python神经网络案例——FC全连接神经网络实现mnist手写体识别

时间:2020-04-23 14:43:52

相关推荐

python神经网络案例——FC全连接神经网络实现mnist手写体识别

全栈工程师开发手册 (作者:栾鹏)

python教程全解

FC全连接神经网络的理论教程参考

/luanpeng825485697/article/details/79009223

加载样本数据集

首先我们要有手写体的数据集文件

t10k-images.idx3-ubyte

t10k-labels.idx1-ubyte

train-images.idx3-ubyte

train-labels.idx1-ubyte

我们实现一个MNIST.py文件,专门用来读取手写体文件中的数据。

# -*- coding: UTF-8 -*-# 获取手写数据。# 28*28的图片对象。每个图片对象根据需求是否转化为长度为784的横向量# 每个对象的标签为0-9的数字,one-hot编码成10维的向量import numpy as np# 数据加载器基类。派生出图片加载器和标签加载器class Loader(object):# 初始化加载器。path: 数据文件路径。count: 文件中的样本个数def __init__(self, path, count):self.path = pathself.count = count# 读取文件内容def get_file_content(self):print(self.path)f = open(self.path, 'rb')content = f.read() # 读取字节流f.close()return content # 字节数组# 将unsigned byte字符转换为整数。python3中bytes的每个分量读取就会变成int# def to_int(self, byte):#return struct.unpack('B', byte)[0]# 图像数据加载器class ImageLoader(Loader):# 内部函数,从文件字节数组中获取第index个图像数据。文件中包含所有样本图片的数据。def get_picture(self, content, index):start = index * 28 * 28 + 16 # 文件头16字节,后面每28*28个字节为一个图片数据picture = []for i in range(28):picture.append([]) # 图片添加一行像素for j in range(28):byte1 = content[start + i * 28 + j]picture[i].append(byte1) # python3中本来就是int# picture[i].append(self.to_int(byte1)) # 添加一行的每一个像素return picture # 图片为[[x,x,x..][x,x,x...][x,x,x...][x,x,x...]]的列表# 将图像数据转化为784的行向量形式def get_one_sample(self, picture):sample = []for i in range(28):for j in range(28):sample.append(picture[i][j])return sample# 加载数据文件,获得全部样本的输入向量。onerow表示是否将每张图片转化为行向量def load(self,onerow=False):content = self.get_file_content() # 获取文件字节数组data_set = []for index in range(self.count): #遍历每一个样本onepic =self.get_picture(content, index) # 从样本数据集中获取第index个样本的图片数据,返回的是二维数组if onerow: onepic = self.get_one_sample(onepic) # 将图像转化为一维向量形式data_set.append(onepic)return data_set# 标签数据加载器class LabelLoader(Loader):# 加载数据文件,获得全部样本的标签向量def load(self):content = self.get_file_content() # 获取文件字节数组labels = []for index in range(self.count): #遍历每一个样本onelabel = content[index + 8] # 文件头有8个字节onelabelvec = self.norm(onelabel) #one-hot编码labels.append(onelabelvec)return labels# 内部函数,one-hot编码。将一个值转换为10维标签向量def norm(self, label):label_vec = []# label_value = self.to_int(label)label_value = label # python3中直接就是intfor i in range(10):if i == label_value:label_vec.append(0.9)else:label_vec.append(0.1)return label_vec# 获得训练数据集。onerow表示是否将每张图片转化为行向量def get_training_data_set(num,onerow=False):image_loader = ImageLoader('train-images.idx3-ubyte', num) # 参数为文件路径和加载的样本数量label_loader = LabelLoader('train-labels.idx1-ubyte', num) # 参数为文件路径和加载的样本数量return image_loader.load(onerow), label_loader.load()# 获得测试数据集。onerow表示是否将每张图片转化为行向量def get_test_data_set(num,onerow=False):image_loader = ImageLoader('t10k-images.idx3-ubyte', num) # 参数为文件路径和加载的样本数量label_loader = LabelLoader('t10k-labels.idx1-ubyte', num) # 参数为文件路径和加载的样本数量return image_loader.load(onerow), label_loader.load()# 将一行784的行向量,打印成图形的样式def printimg(onepic):onepic=onepic.reshape(28,28)for i in range(28):for j in range(28):if onepic[i,j]==0: print(' ',end='')else: print('* ',end='')print('')if __name__=="__main__":train_data_set, train_labels = get_training_data_set(100) # 加载训练样本数据集,和one-hot编码后的样本标签数据集train_data_set = np.array(train_data_set)train_labels = np.array(train_labels)onepic = train_data_set[12] # 取一个样本printimg(onepic) # 打印出来这一行所显示的图片print(train_labels[12].argmax()) # 打印样本标签

我们尝试运行一下。读取第13个样本的内容。

可以看到打印输出样式如下。

建立全连接网络模块

我们使用以下代码实现全连接网络模型,存储为DNN.py

# 实现神经网络反向传播算法,以此来训练网络# 所谓向量化编程,就是使用矩阵运算。import randomimport numpy as npimport datetime# 1. 当为array的时候,默认d*f就是对应元素的乘积,multiply也是对应元素的乘积,dot(d,f)会转化为矩阵的乘积, dot点乘意味着相加,而multiply只是对应元素相乘,不相加# 2. 当为mat的时候,默认d*f就是矩阵的乘积,multiply转化为对应元素的乘积,dot(d,f)为矩阵的乘积# Sigmoid激活函数类class SigmoidActivator(object):def forward(self, weighted_input): #前向传播计算输出return 1.0 / (1.0 + np.exp(-weighted_input))def backward(self, output): #后向传播计算导数return np.multiply(output,(1 - output)) # 对应元素相乘# 全连接每层的实现类。输入对象x、神经层输出a、输出y均为列向量class FullConnectedLayer(object):# 构造函数。input_size: 本层输入向量的维度。output_size: 本层输出向量的维度。activator: 激活函数def __init__(self, input_size, output_size,activator):self.input_size = input_sizeself.output_size = output_sizeself.activator = activator# 权重数组Wself.W = np.random.uniform(-0.1, 0.1,(output_size, input_size)) #初始化为-0.1~0.1之间的数。权重的大小。行数=输出个数,列数=输入个数。a=w*x,a和x都是列向量# 偏置项bself.b = np.zeros((output_size, 1)) # 全0列向量偏重项# 输出向量self.output = np.zeros((output_size, 1)) #初始化为全0列向量# 前向计算,预测输出。input_array: 输入向量,维度必须等于input_sizedef forward(self, input_array): # 式2self.input = input_arrayself.output = self.activator.forward(np.dot(self.W, input_array) + self.b)# 反向计算W和b的梯度。delta_array: 从上一层传递过来的误差项。列向量def backward(self, delta_array):# 式8self.delta = np.multiply(self.activator.backward(self.input),np.dot(self.W.T, delta_array)) #计算当前层的误差,已被上一层使用self.W_grad = np.dot(delta_array, self.input.T) # 计算w的梯度。梯度=误差.*输入self.b_grad = delta_array #计算b的梯度# 使用梯度下降算法更新权重def update(self, learning_rate):self.W += learning_rate * self.W_gradself.b += learning_rate * self.b_grad# 神经网络类class Network(object):# 初始化一个全连接神经网络。layers:数组,描述神经网络每层节点数。包含输入层节点个数、隐藏层节点个数、输出层节点个数def __init__(self, layers):self.layers = []for i in range(len(layers) - 1):self.layers.append(FullConnectedLayer(layers[i], layers[i+1],SigmoidActivator())) # 创建全连接层,并添加到layers中# 训练函数。labels: 样本标签矩阵。data_set: 输入样本矩阵。rate: 学习速率。epoch: 训练轮数def train(self, labels, data_set, rate, epoch):for i in range(epoch):for d in range(len(data_set)):# print(i,'次迭代,',d,'个样本')oneobject = np.array(data_set[d]).reshape(-1,1) #将输入对象和输出标签转化为列向量onelabel = np.array(labels[d]).reshape(-1,1)self.train_one_sample(onelabel,oneobject, rate)# 内部函数,用一个样本训练网络def train_one_sample(self, label, sample, rate):# print('样本:\n',sample)self.predict(sample) # 根据样本对象预测值self.calc_gradient(label) # 计算梯度self.update_weight(rate) # 更新权重# 使用神经网络实现预测。sample: 输入样本def predict(self, sample):sample = sample.reshape(-1,1) #将样本转换为列向量output = sample # 输入样本作为输入层的输出for layer in self.layers:# print('权值:',layer.W,layer.b)layer.forward(output) # 逐层向后计算预测值。因为每层都是线性回归output = layer.output# print('预测输出:', output)return output# 计算每个节点的误差。label为一个样本的输出向量,也就对应了最后一个所有输出节点输出的值def calc_gradient(self, label):# print('计算梯度:',self.layers[-1].activator.backward(self.layers[-1].output).shape)delta = np.multiply(self.layers[-1].activator.backward(self.layers[-1].output),(label - self.layers[-1].output)) #计算输出误差# print('输出误差:', delta.shape)for layer in self.layers[::-1]:layer.backward(delta) # 逐层向前计算误差。计算神经网络层和输入层误差delta = layer.delta# print('当前层误差:', delta.shape)return delta# 更新每个连接权重def update_weight(self, rate):for layer in self.layers: # 逐层更新权重layer.update(rate)# ====================================以上为网络的类构建=================================# ====================================以下为网络的应用=================================# 根据返回结果计算所属类型def valye2type(vec):return vec.argmax(axis=0) # 获取概率最大的分类,由于vec是列向量,所以这里按列取最大的位置# 使用错误率来对网络进行评估def evaluate(network, test_data_set, test_labels):error = 0total = test_data_set.shape[0]for i in range(total):label = valye2type(test_labels[i])predict = valye2type(network.predict(test_data_set[i]))if label != predict:error += 1return float(error) / float(total)# 由于使用了逻辑回归函数,所以只能进行分类识别。识别ont-hot编码的结果if __name__ == '__main__':# 使用神经网络实现and运算data_set = np.array([[0,0],[0,1],[1,0],[1,1]])labels = np.array([[1,0],[1,0],[1,0],[0,1]])# print(data_set)# print(labels)net = Network([2,1,2]) # 输入节点2个(偏量b会自动加上),神经元1个,输出节点2个。net.train(labels, data_set, 2, 100)for layer in net.layers: # 网络层总不包含输出层print('W:',layer.W)print('b:',layer.b)# 对结果进行预测sample = np.array([[1,1]])result = net.predict(sample)type = valye2type(result)print('分类:',type)

训练手写体识别网络模型

# 使用全连接神经网络类,和手写数据加载器,实现验证码识别。import datetimeimport numpy as npimport DNN # 引入全连接神经网络import MNIST # 引入手写数据加载器# 最后实现我们的训练策略:每训练10轮,评估一次准确率,当准确率开始下降时终止训练def train_and_evaluate():last_error_ratio = 1.0epoch = 0train_data_set, train_labels = MNIST.get_training_data_set(6000,True) # 加载训练样本数据集,和one-hot编码后的样本标签数据集test_data_set, test_labels = MNIST.get_test_data_set(1000,True) # 加载测试特征数据集,和one-hot编码后的测试标签数据集train_data_set=np.array(train_data_set)train_labels=np.array(train_labels)test_data_set=np.array(test_data_set)test_labels=np.array(test_labels)print('样本数据集的个数:%d' % len(train_data_set))print('测试数据集的个数:%d' % len(test_data_set))network = work([784, 300, 10]) # 定义一个输入节点784+1,神经元300,输出10while True: # 迭代至准确率开始下降epoch += 1 # 记录迭代次数network.train(train_labels, train_data_set, 0.3, 1) # 使用训练集进行训练。0.3为学习速率,1为迭代次数print('%s epoch %d finished' % (datetime.datetime.now(), epoch)) # 打印时间和迭代次数if epoch % 10 == 0: # 每训练10次,就计算一次准确率error_ratio = DNN.evaluate(network, test_data_set, test_labels) # 计算准确率print('%s after epoch %d, error ratio is %f' % (datetime.datetime.now(), epoch, error_ratio)) # 打印输出错误率if error_ratio < 0.1: # 如果错误率开始上升就不再训练了。breakelse:print('错误率:', last_error_ratio)last_error_ratio = error_ratio # 否则继续训练index=0for layer in network.layers:np.savetxt('MNIST—W'+str(index),layer.W)np.savetxt('MNIST—b' + str(index), layer.b)index+=1print(layer.W)print(layer.b)if __name__ == '__main__':train_and_evaluate() # 使用样本数据集进行预测

由于样本数据集非常大,所以训练速度非常慢。尝试了以下,6000个样本训练一次需要14s。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。