Python利用三层神经网络实现手写数字分类详解
作者:椰麦吸 发布时间:2023-10-06 19:53:40
前言
本文做的是基于三层神经网络实现手写数字分类,神经网络设计是设计复杂深度学习算法应用的基础,本文将介绍如何设计一个三层神经网络模型来实现手写数字分类。首先介绍如何利用高级编程语言Python搭建神经网络训练和推断框架来实现手写数字分类的训练和使用。
本文实验文档下载
一、神经网络组成
一个完整的神经网络通常由多个基本的网络层堆叠而成。本实验中的三层全连接神经网络由三个全连接层构成,在每两个全连接层之间会插入ReLU激活函数引入非线性变换,最后使用Softmax层计算交叉嫡损失,如下图所示。因此本实验中使用的基本单元包括全连接层、ReLU激活函数、Softmax损失函数。
二、代码实现
1.引入库
import numpy as np
import struct
import os
2.导入数据集
MNIST_DIR = "mnist_data"
TRAIN_DATA = "train-images-idx3-ubyte"
TRAIN_LABEL = "train-labels-idx1-ubyte"
TEST_DATA = "t10k-images-idx3-ubyte"
TEST_LABEL = "t10k-labels-idx1-ubyte"
数据集链接
数据集下载后一定记得解压
3.全连接层
class FullyConnectedLayer(object):
def __init__(self, num_input, num_output): # 全连接层初始化
self.num_input = num_input
self.num_output = num_output
def init_param(self, std=0.01): # 参数初始化
self.weight = np.random.normal(loc=0, scale=std, size=(self.num_input, self.num_output))
self.bias = np.zeros([1, self.num_output])
def forward(self, input): # 前向传播计算
self.input = input
self.output = np.dot(self.input,self.weight)+self.bias
return self.output
def backward(self, top_diff): # 反向传播的计算
self.d_weight =np.dot(self.input.T,top_diff)
self.d_bias = top_diff #
bottom_diff = np.dot(top_diff,self.weight.T)
return bottom_diff
def update_param(self, lr): # 参数更新
self.weight = self.weight - lr * self.d_weight
self.bias = self.bias - lr * self.d_bias
def load_param(self, weight, bias): # 参数加载
assert self.weight.shape == weight.shape
assert self.bias.shape == bias.shape
self.weight = weight
self.bias = bias
def save_param(self): # 参数保存
return self.weight, self.bias
4.ReLU激活函数层
class ReLULayer(object):
def forward(self, input): # 前向传播的计算
self.input = input
output = np.maximum(self.input,0)
return output
def backward(self, top_diff): # 反向传播的计算
b = self.input
b[b>0] =1
b[b<0] = 0
bottom_diff = np.multiply(b,top_diff)
return bottom_diff
5.Softmax损失层
class SoftmaxLossLayer(object):
def forward(self, input): # 前向传播的计算
input_max = np.max(input, axis=1, keepdims=True)
input_exp = np.exp(input- input_max)#(64,10)
partsum = np.sum(input_exp,axis=1)
sum = np.tile(partsum,(10,1))
self.prob = input_exp / sum.T
return self.prob
def get_loss(self, label): # 计算损失
self.batch_size = self.prob.shape[0]
self.label_onehot = np.zeros_like(self.prob)
self.label_onehot[np.arange(self.batch_size), label] = 1.0
loss = -np.sum(self.label_onehot*np.log(self.prob)) / self.batch_size
return loss
def backward(self): # 反向传播的计算
bottom_diff = (self.prob - self.label_onehot)/self.batch_size
return bottom_diff
6.网络训练与推断模块
class MNIST_MLP(object):
def __init__(self, batch_size=64, input_size=784, hidden1=32, hidden2=16, out_classes=10, lr=0.01, max_epoch=1,print_iter=100):
self.batch_size = batch_size
self.input_size = input_size
self.hidden1 = hidden1
self.hidden2 = hidden2
self.out_classes = out_classes
self.lr = lr
self.max_epoch = max_epoch
self.print_iter = print_iter
def shuffle_data(self):
np.random.shuffle(self.train_data)
def build_model(self): # 建立网络结构
self.fc1 = FullyConnectedLayer(self.input_size, self.hidden1)
self.relu1 = ReLULayer()
self.fc2 = FullyConnectedLayer(self.hidden1, self.hidden2)
self.relu2 = ReLULayer()
self.fc3 = FullyConnectedLayer(self.hidden2, self.out_classes)
self.softmax = SoftmaxLossLayer()
self.update_layer_list = [self.fc1, self.fc2, self.fc3]
def init_model(self):
for layer in self.update_layer_list:
layer.init_param()
def forward(self, input): # 神经网络的前向传播
h1 = self.fc1.forward(input)
h1 = self.relu1.forward(h1)
h2 = self.fc2.forward(h1)
h2 = self.relu2.forward(h2)
h3 = self.fc3.forward(h2)
self.prob = self.softmax.forward(h3)
return self.prob
def backward(self): # 神经网络的反向传播
dloss = self.softmax.backward()
dh2 = self.fc3.backward(dloss)
dh2 = self.relu2.backward(dh2)
dh1 = self.fc2.backward(dh2)
dh1 = self.relu1.backward(dh1)
dh1 = self.fc1.backward(dh1)
def update(self, lr):
for layer in self.update_layer_list:
layer.update_param(lr)
def load_mnist(self, file_dir, is_images='True'):
bin_file = open(file_dir, 'rb')
bin_data = bin_file.read()
bin_file.close()
if is_images:
fmt_header = '>iiii'
magic, num_images, num_rows, num_cols = struct.unpack_from(fmt_header, bin_data, 0)
else:
fmt_header = '>ii'
magic, num_images = struct.unpack_from(fmt_header, bin_data, 0)
num_rows, num_cols = 1, 1
data_size = num_images * num_rows * num_cols
mat_data = struct.unpack_from('>' + str(data_size) + 'B', bin_data, struct.calcsize(fmt_header))
mat_data = np.reshape(mat_data, [num_images, num_rows * num_cols])
return mat_data
def load_data(self):
train_images = self.load_mnist(os.path.join(MNIST_DIR, TRAIN_DATA), True)
train_labels = self.load_mnist(os.path.join(MNIST_DIR, TRAIN_LABEL), False)
test_images = self.load_mnist(os.path.join(MNIST_DIR, TEST_DATA), True)
test_labels = self.load_mnist(os.path.join(MNIST_DIR, TEST_LABEL), False)
self.train_data = np.append(train_images, train_labels, axis=1)
self.test_data = np.append(test_images, test_labels, axis=1)
def load_model(self, param_dir):
params = np.load(param_dir).item()
self.fc1.load_param(params['w1'], params['b1'])
self.fc2.load_param(params['w2'], params['b2'])
self.fc3.load_param(params['w3'], params['b3'])
def save_model(self, param_dir):
params = {}
params['w1'], params['b1'] = self.fc1.save_param()
params['w2'], params['b2'] = self.fc2.save_param()
params['w3'], params['b3'] = self.fc3.save_param()
np.save(param_dir, params)
def train(self):
max_batch_1 = self.train_data.shape[0] / self.batch_size
max_batch = int(max_batch_1)
for idx_epoch in range(self.max_epoch):
mlp.shuffle_data()
for idx_batch in range(max_batch):
batch_images = self.train_data[idx_batch * self.batch_size:(idx_batch + 1) * self.batch_size, :-1]
batch_labels = self.train_data[idx_batch * self.batch_size:(idx_batch + 1) * self.batch_size, -1]
prob = self.forward(batch_images)
loss = self.softmax.get_loss(batch_labels)
self.backward()
self.update(self.lr)
if idx_batch % self.print_iter == 0:
print('Epoch %d, iter %d, loss: %.6f' % (idx_epoch, idx_batch, loss))
def evaluate(self):
pred_results = np.zeros([self.test_data.shape[0]])
for idx in range(int(self.test_data.shape[0] / self.batch_size)):
batch_images = self.test_data[idx * self.batch_size:(idx + 1) * self.batch_size, :-1]
prob = self.forward(batch_images)
pred_labels = np.argmax(prob, axis=1)
pred_results[idx * self.batch_size:(idx + 1) * self.batch_size] = pred_labels
accuracy = np.mean(pred_results == self.test_data[:, -1])
print('Accuracy in test set: %f' % accuracy)
7.完整流程
if __name__ == '__main__':
h1, h2, e = 128, 64, 20
mlp = MNIST_MLP(hidden1=h1, hidden2=h2,max_epoch=e)
mlp.load_data()
mlp.build_model()
mlp.init_model()
mlp.train()
mlp.save_model('mlp-%d-%d-%depoch.npy' % (h1,h2,e))
mlp.load_model('mlp-%d-%d-%depoch.npy' % (h1, h2, e))
mlp.evaluate()
三、代码debug
pycharm在初次运行时,会在以下代码报错:
mlp.load_model('mlp-%d-%d-%depoch.npy' % (h1, h2, e))
ValueError: Object arrays cannot be loaded when allow_pickle=False
经过上网查看原因后,发现是numpy版本太高引起
解决方法:
点击报错处,进入源代码(.py),注释掉693行:
#if not allow_pickle:
#raise ValueError("Object arrays cannot be loaded when "
# "allow_pickle=False")
# Now read the actual data.
if dtype.hasobject:
# The array contained Python objects. We need to unpickle the data.
#if not allow_pickle:
#raise ValueError("Object arrays cannot be loaded when "
# "allow_pickle=False")
if pickle_kwargs is None:
pickle_kwargs = {}
try:
array = pickle.load(fp, **pickle_kwargs)
except UnicodeError as err:
if sys.version_info[0] >= 3:
# Friendlier error message
四、结果展示
在不改变网络结构的条件下我通过自行调节参数主要体现在:
if __name__ == '__main__':
h1, h2, e = 128, 64, 20
class MNIST_MLP(object):
def __init__(self, batch_size=64, input_size=784, hidden1=32, hidden2=16, out_classes=10, lr=0.01, max_epoch=1,print_iter=100):
为了提高准确率,当然你可以从其他方面进行修改,以下是我得出的输出结果:
补充
ValueError: Object arrays cannot be loaded when allow_pickle=False解决方案
在读.npz文件时报下面错误:
population_data=np.load("./data/populations.npz")
print(population_data.files)#里面有两个数组 data feature_names
data=population_data['data']
print(data)
print(population_data['feature_names'])
报错:
['data', 'feature_names']
Traceback (most recent call last):
File "E:/pycharm file/使用scikit-learn构建模型/构建一元线性模型.py", line 32, in <module>
data=population_data['data']
File "E:\pycharm file\venv\lib\site-packages\numpy\lib\npyio.py", line 262, in __getitem__
pickle_kwargs=self.pickle_kwargs)
File "E:\pycharm file\venv\lib\site-packages\numpy\lib\format.py", line 692, in read_array
raise ValueError("Object arrays cannot be loaded when "
ValueError: Object arrays cannot be loaded when allow_pickle=False
报错为:numpy版本太高,我用的是1.16.3,应该降级为1.16.2
两种解决方案:
Numpy 1.16.3几天前发布了。从发行版本中说明:“函数np.load()和np.lib.format.read_array()采用allow_pickle关键字,现在默认为False以响应CVE-2019-6446 < nvd.nist.gov/vuln/detail / CVE-2019-6446 >“。降级到1.16.2对我有帮助,因为错误发生在一些library内部
第一种:点击报错处,进入源代码(.py),注释掉693行:
#if not allow_pickle:
#raise ValueError("Object arrays cannot be loaded when "
# "allow_pickle=False")
# Now read the actual data.
if dtype.hasobject:
# The array contained Python objects. We need to unpickle the data.
#if not allow_pickle:
#raise ValueError("Object arrays cannot be loaded when "
# "allow_pickle=False")
if pickle_kwargs is None:
pickle_kwargs = {}
try:
array = pickle.load(fp, **pickle_kwargs)
except UnicodeError as err:
if sys.version_info[0] >= 3:
# Friendlier error message
修改后成功解决了问题,但改掉源码不知道会不会有后遗症
第二种:降级numpy版本
pip install numpy==1.16.2
上述两种方法都可以成功解决报错问题
来源:https://blog.csdn.net/qq_50492541/article/details/121459607


猜你喜欢
- 今天介绍一下 go语言的并发机制以及它所使用的CSP并发模型CSP并发模型CSP模型是上个世纪七十年代提出的,用于描述两个独立的并发实体通过
- json 作为一种通用的编解码协议,可阅读性上比 thrift,protobuf 等协议要好一些,同时编码的 size 也会比 xml 这类
- linecache模块接触到linecache这个模块是因为前两天读attrs源码的时候看到内部代码引用了这个模块来模拟一个假文件,带着一脸
- openCV是一个开源的用C/C++开发的计算机图形图像库,非常强大,研究资料很齐全。本文重点是介绍如何使用php来调用其中的局部的功能。人
- 【人工智能项目】混合高斯模型运动目标检测本次工作主要对视频中运动中的人或物的边缘背景进行检测。那么走起来瓷!!!原视频高斯算法提取工作imp
- chomp是用来删除换行符.#!/usr/bin/perl $c="abcde"; chomp($c); print &
- 在本章中,我们将重点介绍RSA密码加密的不同实现及其所涉及的功能.您可以引用或包含此python文件以实现RSA密码算法实现.加密算法模块&
- mysql8.0.12安装教程,分享给大家。一.安装1.从网上下载MySQL8.0.12版本,下载地址2. 下载完成后解压我解压的路径是:D
- 1.1.1 摘要 如果说要对数据库进行优化,我们主要可以通过以下五种方法,对数据库系统进行优化。 1. 计算机硬件调优 2. 应用程序调优
- 如何让我的网页自动适应客户端的屏幕分辨率?然后用下列办法进行自动推送:<% @language="vbscript
- 概述相信我们经常会遇到这样的场景:想要了解双十一天猫购买化妆品的人员中平均消费额度是多少(这可能有利于对商品价格区间的定位);或者不同年龄段
- 前序1、cookie介绍Cookie是一段不超过4KB的小型文本数据,保存在客户端浏览器中,由一个名称(Name)、一个值(Value)和其
- MySQL自定义序列数实现往往有很多情况下,我们需要使用自己生成的唯一Id或保证不重复的序列号,特别是在高并发的场景下。为此,很容易想到的实
- '************************************* 'asp计算随机数&nb
- 1. 在linux上安装psiblast最好新建一个python环境,因为我发现conda安 * last默认的是python==3.6.11
- time模块1:概述时间表示的分类时间戳格式化的时间字符串结构化时间时间戳:时间戳表示的是从1970年1月1日整0点到目前秒的偏移量,数据类
- CSS浮动一直是个比较让人郁闷的问题,很多的布局问题都出在浮动上,特别是当浮动的列数很多时,但其实只要理解了两列结构的浮动,面对多列数的浮动
- 本文实例讲述了Python企业编码生成系统之系统主要函数设计。分享给大家供大家参考,具体如下:一 主要函数功能描述函数功能mkdir判断保存
- 1.在查询分析器理启动或停止SQL Agent服务启动:use mastergoxp_cmdshell 'net start SQL
- 前言PHP 中有个释放变量的语句叫做unset(从PHP4开始unset已经不再是一个函数了,而是一个语句),本文主要给大家介绍了关于php