该数据集可以在Yann LeCun的官网上查看。官网链接:手写数字识别数据集。他这个数据集保存形式比较特殊,四个文件(训练集、测试集的图片和标签)都是以IDX文件格式保存的。IDX文件格式是各种数值类型的向量和多维矩阵的简单格式。

以官网的train-images.idx3-ubyte为例来说明IDX格式。
为了直观看到图片,我们需要将pixel数据可视化。而且我对IDX文件不是很了解,更倾向于对图片数据进行处理,于是在网上找了下面的程序,用以转化。
将四个数据集文件放在同一个目录下,运行下面的代码,可以生成mnist_train的文件夹,里面有0-9个子文件,每个子文件都有对应的图片。
理解好上面介绍的文件格式和unpack_from缓存流的用法,就理解了data_file_size要改成什么值以及为什么要改变值。
import numpy as npimport struct from PIL import Imageimport os data_file = 'train-images.idx3-ubyte'# It's 47040016B, but we should set to 47040000Bdata_file_size = 47040016data_file_size = str(data_file_size - 16) + 'B' data_buf = open(data_file, 'rb').read() magic, numImages, numRows, numColumns = struct.unpack_from( '>IIII', data_buf, 0)datas = struct.unpack_from( '>' + data_file_size, data_buf, struct.calcsize('>IIII'))datas = np.array(datas).astype(np.uint8).reshape( numImages, 1, numRows, numColumns) label_file = 'train-labels.idx1-ubyte' # It's 60008B, but we should set to 60000Blabel_file_size = 60008label_file_size = str(label_file_size - 8) + 'B' label_buf = open(label_file, 'rb').read() magic, numLabels = struct.unpack_from('>II', label_buf, 0)labels = struct.unpack_from( '>' + label_file_size, label_buf, struct.calcsize('>II'))labels = np.array(labels).astype(np.int64) datas_root = 'mnist_train'if not os.path.exists(datas_root): os.mkdir(datas_root) for i in range(10): file_name = datas_root + os.sep + str(i) if not os.path.exists(file_name): os.mkdir(file_name) for ii in range(numLabels): img = Image.fromarray(datas[ii, 0, 0:28, 0:28]) label = labels[ii] file_name = datas_root + os.sep + str(label) + os.sep + \ 'mnist_train_' + str(ii) + '.png' img.save(file_name)与训练集的代码差不多,改了改文件名字和大小而已。
import numpy as npimport struct from PIL import Imageimport os data_file = 't10k-images.idx3-ubyte'# It's 7840016B, but we should set to 7840000Bdata_file_size = 7840016data_file_size = str(data_file_size - 16) + 'B' data_buf = open(data_file, 'rb').read() magic, numImages, numRows, numColumns = struct.unpack_from( '>IIII', data_buf, 0)datas = struct.unpack_from( '>' + data_file_size, data_buf, struct.calcsize('>IIII'))datas = np.array(datas).astype(np.uint8).reshape( numImages, 1, numRows, numColumns) label_file = 't10k-labels.idx1-ubyte' # It's 10008B, but we should set to 10000Blabel_file_size = 10008label_file_size = str(label_file_size - 8) + 'B' label_buf = open(label_file, 'rb').read() magic, numLabels = struct.unpack_from('>II', label_buf, 0)labels = struct.unpack_from( '>' + label_file_size, label_buf, struct.calcsize('>II'))labels = np.array(labels).astype(np.int64) datas_root = 'mnist_test'if not os.path.exists(datas_root): os.mkdir(datas_root) for i in range(10): file_name = datas_root + os.sep + str(i) if not os.path.exists(file_name): os.mkdir(file_name) for ii in range(numLabels): img = Image.fromarray(datas[ii, 0, 0:28, 0:28]) label = labels[ii] file_name = datas_root + os.sep + str(label) + os.sep + \ 'mnist_test_' + str(ii) + '.png' img.save(file_name)对于我这个阶段,模型什么的往往都只是套用即可,而data_loader是实现过程中的难点,也是最需要编程的地方,小白应该训练的基本功。
def data_load(mode='train'): data_list = [] # 分别读取 if mode == 'train': dir = 'mnist_train\\0' for filename in os.listdir(dir): img_path = dir + '\\' + filename img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) img = np.reshape(img, [1, IMG_ROWS, IMG_COLS]).astype('float32') label = np.array([0]).astype('int64') data_list.append((img, label)) dir = 'mnist_train\\1' for filename in os.listdir(dir): img_path = dir + '\\' + filename img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) img = np.reshape(img, [1, IMG_ROWS, IMG_COLS]).astype('float32') label = np.array([1]).astype('int64') data_list.append((img, label)) # 打乱 random.shuffle(data_list) if mode == 'eval': dir = 'mnist_test\\0' for filename in os.listdir(dir): img_path = dir + '\\' + filename img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) img = np.reshape(img, [1, IMG_ROWS, IMG_COLS]).astype('float32') label = np.array([0]).astype('int64') data_list.append((img, label)) dir = 'mnist_test\\1' for filename in os.listdir(dir): img_path = dir + '\\' + filename img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) img = np.reshape(img, [1, IMG_ROWS, IMG_COLS]).astype('float32') label = np.array([1]).astype('int64') data_list.append((img, label)) imgs_list = [] labels_list = [] for data in data_list: imgs_list.append(data[0]) labels_list.append(data[1]) if len(imgs_list) == BATCHSIZE: yield np.array(imgs_list), np.array(labels_list) imgs_list = [] labels_list = [] if len(imgs_list) > 0: yield np.array(imgs_list), np.array(labels_list) return data_load可以先来测试一下:
# 声明数据读取函数,从训练集中读取数据train_loader = data_load# 以迭代的形式读取数据for batch_id, data in enumerate(train_loader()): image_data, label_data = data if batch_id == 0: # 打印数据shape和类型 print("打印第一个batch数据的维度,以及数据的类型:") print("图像维度: {}, 标签维度: {}, 图像数据类型: {}, 标签数据类型: {}".format(image_data.shape, label_data.shape, type(image_data), type(label_data))) break# 打印第一个batch数据的维度,以及数据的类型:# 图像维度: (100, 1, 28, 28), 标签维度: (100, 1), 图像数据类型: <class 'numpy.ndarray'>, 标签数据类型: <class 'numpy.ndarray'>class MNIST(paddle.nn.Layer): def __init__(self): super(MNIST, self).__init__() self.conv1 = nn.Conv2D(in_channels=1, out_channels=20, kernel_size=5, stride=1, padding=2) self.max_pool1 = nn.MaxPool2D(kernel_size=2, stride=2) self.conv2 = nn.Conv2D(in_channels=20, out_channels=20, kernel_size=5, stride=1, padding=2) self.max_pool2 = nn.MaxPool2D(kernel_size=2, stride=2) self.fc = nn.Linear(in_features=980, out_features=10) def forward(self, inputs, label=None): x = self.conv1(inputs) x = F.relu(x) x = self.max_pool1(x) x = self.conv2(x) x = F.relu(x) x = self.max_pool2(x) x = paddle.reshape(x, [x.shape[0], -1]) x = self.fc(x) if label is not None: acc = paddle.metric.accuracy(input=x, label=label) return x, acc else: return x训练的部分就不是很复杂了,我直接搬的网上的代码,最多改改设定的事情。
def train(model): model.train() train_loader = data_load opt = paddle.optimizer.SGD(learning_rate=0.001, parameters=model.parameters()) EPOCH_NUM = 10 for epoch_id in range(EPOCH_NUM): for batch_id, data in enumerate(train_loader()): images, labels = data images = paddle.to_tensor(images) labels = paddle.to_tensor(labels) predicts = model(images) loss = F.cross_entropy(predicts, labels) avg_loss = paddle.mean(loss) if batch_id % 100 == 0: print("epoch: {}, batch: {}, loss is: {}".format(epoch_id, batch_id, avg_loss.numpy())) avg_loss.backward() opt.step() opt.clear_grad() paddle.save(model.state_dict(), 'mnist2.pdparams') model = MNIST()#启动训练过程train(model)def evaluation(model): print('start evaluation .......') # 定义预测过程 params_file_path = 'mnist2.pdparams' # 加载模型参数 param_dict = paddle.load(params_file_path) model.load_dict(param_dict) model.eval() eval_loader = data_load acc_set = [] avg_loss_set = [] for batch_id, data in enumerate(eval_loader('eval')): images, labels = data images = paddle.to_tensor(images) labels = paddle.to_tensor(labels) predicts, acc = model(images, labels) loss = F.cross_entropy(input=predicts, label=labels) avg_loss = paddle.mean(loss) acc_set.append(float(acc.numpy())) avg_loss_set.append(float(avg_loss.numpy())) #计算多个batch的平均损失和准确率 acc_val_mean = np.array(acc_set).mean() avg_loss_val_mean = np.array(avg_loss_set).mean() print('loss={}, acc={}'.format(avg_loss_val_mean, acc_val_mean))model = MNIST()evaluation(model)想要可视化,可以改进四、五两节的函数,return返回值,绘图。
本文来自博客园,作者:静候佳茵,转载请注明原文链接:https://www.cnblogs.com/hitwherznchjy/p/16092784.html