基于tensorflow的MNIST探索(基于图像版本的实现与探索)——如何读取较大数据集进行训练(一)

版权声明:转载需注明出处 https://blog.csdn.net/holmes_MX/article/details/81912019

目录

 

0. 写作目的

1. 前言

2. 即用即读的训练方式

2.1 数据的准备

2.2 读取数据的类

3. 进行训练(TensorFlow)

3.1 训练的网络

3.2 训练的结果


0. 写作目的

好记性不如烂笔头。

1. 前言

对于较小的数据处理时,我们可以完全读入内存,但是针对较大的数据集,如果一次性读入内存,将会出现一下两点问题:

1) 内存是否够用。

一些较大的数据集,如ImageNet数据集,一次性完全读入内存,需要较大的内存,即使内存足够,我们也不会使用该方法,原因如下第二点。

2) 程序的交互性差

如果我们将较大的数据集一次性读入时,首先在程序运行之前,我们第一步就要读入数据,如果在读入数据后,后面的代码出现了bug,修改bug后,仍需要较大的时间读入,浪费时间。(虽然对于Jupyter notebook或类似的环境修改一下代码,可以避免重新读入,但是读入的过程的仍在浪费时间,而且即使数据全都读入内存,但在一段时间内我们使用的数据仍只是一部分,因此对于内存也造成了一种资源浪费。)

因此,一种较好的方式就是,即用即读。在深度框架中,不同的深度学习框架都有自己的序列化格式,如caffe的LMDB文件,Tensorflow的TFrecord文件等。

在之前使用Caffe的LMDB文件时,我就有一种想法,以图像分类为例,可不可以先保存图像的路径,以及对应的标签,然后在使用时,进行即用即读。本文首先进行了该种方法的尝试。

2. 即用即读的训练方式

2.1 数据的准备

首先下载MNIST的数据,如果下载的不是图像数据集,是train-images-idx3-ubyte.gz,在ubuntu下采用命令:gunzip train-images-idx3-ubyte.gz进行解压,然后通过以下代码[1](基于PyTorch的)来生成图像数据集。以下代码需要在数据目录下运行,运行结果将在数据集目录下,产生两个图像文件夹train和test,同时生成两个txt文件——train.txt 和test.txt,其中图像文件为所有图像,txt的内容是:图像的绝对路径以及对应的标签。

import os
from skimage import io
import torchvision.datasets.mnist as mnist

root=os.getcwd()
train_set = (
    mnist.read_image_file(os.path.join(root, 'train-images-idx3-ubyte')),
    mnist.read_label_file(os.path.join(root, 'train-labels-idx1-ubyte'))
        )
test_set = (
    mnist.read_image_file(os.path.join(root, 't10k-images-idx3-ubyte')),
    mnist.read_label_file(os.path.join(root, 't10k-labels-idx1-ubyte'))
        )
print("training set :",train_set[0].size())
print("test set :",test_set[0].size())

def convert_to_img(train=True):
    if(train):
        f=open(root+'/train.txt','w')
        data_path=root+'/train/'
        if(not os.path.exists(data_path)):
            os.makedirs(data_path)
        for i, (img,label) in enumerate(zip(train_set[0],train_set[1])):
            img_path=data_path+str(i)+'.jpg'
            io.imsave(img_path,img.numpy())
            label = label.numpy()
            f.write(img_path+' '+str(label)+'\n')
            break
        f.close()
    else:
        f = open(root + '/test.txt', 'w')
        data_path = root + '/test/'
        if (not os.path.exists(data_path)):
            os.makedirs(data_path)
        for i, (img,label) in enumerate(zip(test_set[0],test_set[1])):
            img_path = data_path+ str(i) + '.jpg'
            io.imsave(img_path, img.numpy())
            label = label.numpy()
            f.write(img_path + ' ' + str(label) + '\n')
        f.close()

convert_to_img(True)
convert_to_img(False)

2.2 读取数据的类

此部分是在Reference的基础上进一步改进得到的,且发现了shuffle_batch的有趣现象,具体参考我的博客:。这里之介绍修改后的代码。(下面的代码所需要的数据集格式为:train图像文件夹下包含各子类文件夹,各子类文件夹下包含各自的图像。此处与上面代码得到的train文件夹不一样,读者可自行修改代码)

class LoadDatas(object):
    '''
    将所有的图像与label保存为文件,然后从文件中读入
    '''
    def __encode__(self, imageName):
        '''

        :param imageName:  the name of category in real world
        :return:   the encode of label
        '''
        for ii in range(len(self.classes)):
            if imageName == self.classes[ii]:
                return ii
        raise("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!label error!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")

    def __init__(self, dataFilePath, targetHeight, targetWidth, classNumber, flag):
        '''
        dataFilePath: is the data path. its like this: train/ 0 class File, 1 calss File
        targetHeight: is the height of image you want
        targetWidth: is the width of image you want
        classNumber: is the number of calss
        flag: is a sign to show 'train data' or 'test data'
        self.data is a list of [image_file_path, class_label]
        '''
        if (flag == "train") or (flag == 'train'):
            self.flag = 'train'
        elif(flag == 'test' or (flag == "test")):
            self.flag = 'test'
        else:
            raise("!!!!!!!!!!!!!!!!Data Type is Error!!!!!!!!!!!!!")


        self.classes = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
        self.data = []
        self.targetHeight = targetHeight
        self.targetWidth = targetWidth
        self.classNumber = classNumber
        self.length = 0
        self.counter = 0
        for className in os.listdir(dataFilePath):
            for imageName in os.listdir( dataFilePath + '/' + str(className)):
                tempImageName = dataFilePath + '/' + str(className) + '/' + imageName
                self.data.append([tempImageName, self.__encode__(str(className))])
                self.length += 1
        if self.flag == 'train':
            print("read Train Data is Done! train Data number is: {}".format(self.length))
        elif self.flag == 'test':
            print("read Test Data is Done! test Data number is: {}".format(self.length))
        self.data = np.asarray(a=self.data)

    def next_batch(self, batch_size=16):
        '''

        :param batch_size:
        :return:  images is a np.ndarray with shape = [None, targetHeight, targetWidth, 1/3]
                  labes: is a np.ndarray with shape = [None, ]
        '''
        if self.flag == 'train':
            #np.random.shuffle(dataset)
            images = []
            labels = []
            print(self.counter)
            np.random.shuffle(self.data)
            tempData = self.data[:batch_size]
            for item in tempData:
                img_raw = Image.open(item[0])
                img_raw = img_raw.convert("RGB")
                img_raw = img_raw.resize((self.targetHeight, self.targetWidth))
                img_rawArr = np.array(img_raw)
                img_rawArr = img_rawArr.astype(dtype=np.float32) * (1. / 255)
                images.append(img_rawArr)
                labels.append(self.__oneHotLabel__(item[1]))
            return images, labels

        elif self.flag == 'test':
            images = []
            labels = []
            if( self.counter + batch_size > self.length ):
                tempData = self.data[self.counter : self.length]
            else:
                tempData = self.data[self.counter: (self.counter + batch_size)]
            for item in tempData:
                img_raw = Image.open(item[0])
                img_raw = img_raw.convert("RGB")
                img_raw = img_raw.resize((self.targetHeight, self.targetWidth), Image.ANTIALIAS)
                img_rawArr = np.array(img_raw)
                img_rawArr = img_rawArr.astype(dtype=np.float32) * (1. / 255)
                images.append(img_rawArr)
                labels.append(self.__oneHotLabel__(item[1]))
            return images, labels

    def __oneHotLabel__(self, label):
        label_oht = [0 for ii in range(self.classNumber)]
        label_oht[int(label)] = 1
        return label_oht

3. 进行训练(TensorFlow)

3.1 训练的网络

此处的myConv2d, myMaxPooling2D, myFc函数均为基于TensorFlow底层修改得到的,新手可以使用tf.contirb.slim来代替。注意:虽然MNIST数据集比较简单,但是网络对于数据集影响还是很大的,博主刚开始写的一个网络收敛速度较慢(网络有点儿深)。

def defNet(input_tensor, reuse=False):
    conv1 = myConv2d(input_tensor, conv_size=3, output_channel=32, name='conv1', padding='SAME', act=tf.nn.relu,
                     reuse=reuse)
    conv1_p1 = myMaxPooling2D(conv1, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='VALID', name='conv1_p1',
                              reuse=reuse)


    conv2 = myConv2d(conv1_p1, conv_size=3, output_channel=64, name='conv2', padding='SAME', act=tf.nn.relu,
                     reuse=reuse)
    conv2_p2 = myMaxPooling2D(conv2, ksize=[1, 2, 2, 1], strides=[1,2,2,1], padding="VALID", name='conv2_p2', reuse=reuse)


    conv3 = myConv2d(conv2_p2, conv_size=3, output_channel=128, name='conv3', padding='SAME', act=tf.nn.relu, reuse=reuse)
    conv3_p3 = myMaxPooling2D(conv3, ksize=[1,2,2,1], strides=[1,2,2,1], padding='VALID', name='conv3_p3', reuse=reuse)

    flatten_conv = tf.contrib.layers.flatten(conv3_p3)
    print_Layer(flatten_conv)

    fc4 = myFc(flatten_conv, output_channel=128, name='fc4')
    logits = myFc(fc4, output_channel=10, name='logits', act=None)
    return logits

3.2 训练的结果

1) 训练时,IDE端结果的输出

此处不必在意"0”表示什么意思。训练参数为10epoch,每个epoch3000次迭代,优化算法为:AdamOptimizer(0.01)。

2) Tensorboard 端的输出

查看网络结构:

查看训练时的loss以及Acc:

(此处对于loss感觉存在一些问题,训练集和测试集的Acc都很高,但是loss值不是很低,与PyTorch MNIST(epoch3: iter457: loss 0.032229 Acc: 0.990085)训练的结果相比高了不少,后续对此再进行探索)

问题为:在计算loss值时,参数选择有错误,tf.losses.softmax_cross_entropy函数输入为logist和y_true。

后续给出如何使用TFRecord格式来进行训练网络。

There may be some mistakes in this blog. So, any suggestions and comments are welcome!

[Reference]

[1] https://www.cnblogs.com/denny402/p/7520063.html

[2] https://blog.csdn.net/u014484783/article/details/79621811

 

猜你喜欢

转载自blog.csdn.net/holmes_MX/article/details/81912019