摘要
最近看了Micheal Nielsen 的 Neural Networks and Deep Learning, 终于明白了些神经网络的相关原理,所以自己动手,结合网上的博客,书中的示例代码,实现了一下MNIST手写字体识别,写此博文以做记录。
本文将介绍NN的基本原理,TensorFlow实现MNIST 手写字体识别,然后是进一步提高模型是别的准确率,搭建CNN网络,这些实现的代码在网上有很多,但我主要想记录一下初期也就是对ML/DL一窍不通的时候看博客遇到的一些问题。
导入MNIST
网上有很多的博客教怎么导入数据,大多是调用TensorFlow内示例的包,代码如下:
from tensorflow.examples.tutorials.mnist import input_data
mnist=input_data.read_data_sets('MNIST_data', one_hot=True)
这个方法固然好,可以帮助新手忽略一些导入数据的细节,但是想要导入本地的MNIST数据集时,或者从Kaggle下载的CSV文件时,就无能为力了,所以我就结合网上的一段代码,写了个自己的导入函数。
首先需要从网上下载数据集,Yann LeCun's Home Page 给出了数据集的下载链接。从这里下载的数据我是看不懂了,需要进行解码。这里两个函数分别提取图片的信息和标签的信息并数据的格式进行了改变,分别是把图片从(28,28)变为(784,1)和把标签从常量数字变为了向量。
import numpy as np
import struct
# 训练集文件
train_images_idx3_ubyte_file = './MNIST_data/train-images.idx3-ubyte'
# 训练集标签文件
train_labels_idx1_ubyte_file = './MNIST_data/train-labels.idx1-ubyte'
# 测试集文件
test_images_idx3_ubyte_file = './MNIST_data/t10k-images.idx3-ubyte'
# 测试集标签文件
test_labels_idx1_ubyte_file = './MNIST_data/t10k-labels.idx1-ubyte'
# 文件路径为相对路径,需要自己修改
def decode_idx3_ubyte(idx3_ubyte_file):
"""
解析idx3文件的通用函数<-images
:param idx3_ubyte_file: idx3文件路径
:return: 数据集
"""
# 读取二进制数据
bin_data = open(idx3_ubyte_file, 'rb').read()
# 解析文件头信息,依次为魔数、图片数量、每张图片高、每张图片宽
offset = 0
fmt_header = '>iiii'
magic_number, num_images, num_rows, num_cols = struct.unpack_from(fmt_header, bin_data, offset)
# 解析数据集
image_size = num_rows * num_cols
offset += struct.calcsize(fmt_header)
fmt_image = '>' + str(image_size) + 'B'
images = np.empty((num_images, num_rows, num_cols))
for i in range(num_images):
images[i] = np.array(struct.unpack_from(fmt_image, bin_data, offset)).reshape((num_rows, num_cols))
offset += struct.calcsize(fmt_image)
return images.reshape(num_images, 784) # 注意,这里将数据从(28,28)展为(784,1)
def decode_idx1_ubyte(idx1_ubyte_file):
"""
解析idx1文件的通用函数<-labels
:param idx1_ubyte_file: idx1文件路径
:return: 数据集
"""
# 读取二进制数据
bin_data = open(idx1_ubyte_file, 'rb').read()
# 解析文件头信息,依次为魔数和标签数
offset = 0
fmt_header = '>ii'
magic_number, num_images = struct.unpack_from(fmt_header, bin_data, offset)
# 解析数据集
offset += struct.calcsize(fmt_header)
fmt_image = '>B'
labels = np.empty(num_images)
for i in range(num_images):
labels[i] = struct.unpack_from(fmt_image, bin_data, offset)[0]
offset += struct.calcsize(fmt_image)
labels = np.array([vectorized_result(y) for y in labels]) # 用自定义函数,将标签向量化
return labels
下面是向量化标签的函数
def vectorized_result(j):
e = np.zeros((10, 1))
e[int(j)] = 1.0
return e.flatten('C')
该函数使用了np的flatten方法,将(10,1)维的数组展成了向量。
然后调用以上方法分别导入训练数据和测试数据
def load_train_images(idx_ubyte_file=train_images_idx3_ubyte_file):
return decode_idx3_ubyte(idx_ubyte_file)
def load_train_labels(idx_ubyte_file=train_labels_idx1_ubyte_file):
return decode_idx1_ubyte(idx_ubyte_file)
def load_test_images(idx_ubyte_file=test_images_idx3_ubyte_file):
return decode_idx3_ubyte(idx_ubyte_file)
def load_test_labels(idx_ubyte_file=test_labels_idx1_ubyte_file):
return decode_idx1_ubyte(idx_ubyte_file)
以上就是数据的导入过程