基于TensorFlow的CNN模型——猫狗分类识别器(二)之数据预处理

注意:这是一个完整的项目,建议您按照完整的博客顺序阅读。

目录

一、对数据集进行预处理

(1)设计一个预处理程序

(2)设计一个数据存储对象

(3)设计一个转换程序

(4)设计一个可视化程序


一、对数据集进行预处理

在第一步,我们要对数据集进行一些处理,主要任务是“将数据集文件处理成可读取到内存进行特征学习的数据对象(DataSets)”。

我们分析该任务,可以归纳出该阶段工作的主要步骤:

步骤

主要任务

(1)

设计一个预处理程序,将图片读取到内存并进行缩放等预处理。

(2)

设计一个数据存储对象,用来存储训练集、验证集和测试集。

(3)

设计一个转换程序,用来将内存数据转换成统一的数据对象。

(4)

设计一个可视化程序,用来可视化预处理后的图片数据,验证数据处理是否合适(附加)

(1)设计一个预处理程序

现在假设我们已经有了一份数据集文件,其文件结构是:

我们的目标是将这份文件读取到内存中,同时保留它的图片数据信息、图片标签信息、图片标签语义信息、图片名称信息等。

现在先考虑将文件读取到内存中,要读取文件先得知道文件所在路径,所以我们第一步是得到一个图片文件路径的列表。我们先来看下图片文件的路径特征

'training_data/cats/cat.1000'和'training_data/dogs/dog.1000'
'testing_data/cats/cat.0'和'testing_data/dogs/dog.0'

我们可以通过os.path.join()和glob.glob()函数获取所需要的路径特征:

#data_path='training_data'/'testing_data'
#fields='dogs'/ 'cats'
path = os.path.join(data_path, fields, '*g')  # 图片文件的格式
files = glob.glob(path)  # 返回符合规则的文件路径列表

有了路径,我们就可以获取相应的图像数据:

image = cv2.imread(image_path)  # 读取图片,返回一个三维数组:shape:(333,500,3)
image = cv2.resize(image, (image_size, image_size), 0, 0, cv2.INTER_LINEAR)  # 图像缩放大小 shape:(64,64,3)
image = image.astype('float32')  # 转为浮点数
image = np.multiply(image, 1.0 / 255.0)  # 为了plt.imshow可显示
image = np.array(image)# 列表转换为数组
plt.imshow(image)# 显示图像数据 shape:(64,64,3)

首先,cv2.imread()从指定路径读取图像数据,返回一个三维数组,例如该数组的shape=(333,500,3),则表示这是一张长333像素,宽500像素的具有RGB三通道的彩色图像,这个三维数组可以理解为,这个数组中有333个二维数组,每个二维数组中有500个一维数组,每个一维数组中代表RGB数值的3个数,例如:该数组中共有333个如下二维数组:

[[50 79 210]...
[13 31 32] [ 8 26 27]]

然后,cv2.resize()对该图像数据进行缩放,多余或不足的地方进行插值处理,三维数组的shape=(64,64,3)。

另外,np.multiply(image, 1.0 / 255.0)可以将这个三维数组中的每个RGB值从0-255转换到0-1之间,这种处理是因为plt.imshow(image)要求显示的图像数据的RGB在0-1之间。

(2)设计一个数据存储对象

读取到的所有图像数据,我们将会存储到一个数据对象DataSets中,其主要有三个主要变量,分别存储训练集、验证集和测试集对象。

"""图片全部数据集对象"""
class DataSets(object):
    def __init__(self, train=None, valid=None,test = None):
        self.__train = train  # 训练数据集
        self.__valid = valid  # 验证数据集
        self.__test = test # 测试数据集

其中的每个数据集对象都可以通过如下方式访问和修改:

@property #表示该方法可以直接当属性访问,不可以修改
def train(self):
    return self.__train
def set_train(self,train):
    self.__train = train

里面的每个数据集对象设计为一个DataSet对象,其最关键的有如下四类信息,images就是上一小节读取的多个三维数组,即四维数组,如shape=(100,64,64,3)就是表示存储了100个64*64的RGB图像。

class DataSet(object):
    def __init__(self,images,labels,img_names,cls):
        # 存储的图片相关信息
        self._images = images # 图片数据
        self._labels = labels # 图片标签(one-hot编码)
        self._img_names = img_names # 图片名称
        self._cls = cls  # 图片标签语义,例如'cats','dogs',为了方便

DataSet对象还有一个非常关键的next_batch(batch_size)方法,用来从数据集中获取batch_size个数据用于训练或者测试,该方法主要通过如下代码实现:

def next_batch(self,batch_size):
    assert batch_size <= self._num_examples # 检测批次大小是否合法
    start = self._index_in_epoch # 初始序号,从0开始
    self._index_in_epoch += batch_size # 预计终止序号
    # 检测要采取的终止序号是否超过样本总数
    if self._index_in_epoch > self._num_examples:
        self._epochs_done += 1 # 取完次数+1
        start = 0 # 从头开始数
        self._index_in_epoch = batch_size #
    end = self._index_in_epoch # 确定终止序号
    return self._images[start:end],self._labels[start:end],self._img_names[start:end],self.cls[start:end]

(3)设计一个转换程序

在前面两节我们定义了图片的预处理程序以及我们最终要生成的DataSets对象,现在我们可以先继续看下如何得到一个DataSet的基本信息,我们直接遍历第一小节获取到的路径列表files:

for f1 in files:
    # 读取图片
    image =preprocessed_image(f1,self.image_size)
    images.append(image)
    # 设置标签(One-Hot编码形式)
    label = np.zeros(len(self.classes))
    label[index] = 1.0
    labels.append(label)
    # 存储图片文件名和标签语义
    flbase = os.path.basename(f1)  # 返回path最后的文件名
    img_names.append(flbase)
    cls.append(fields)

经过上述循环程序,我们得到了images、labels、img_names、cls共计4个四维数组,然后就可以直接得到测试集了:

data_sets.set_test(DataSet(test_images, test_labels, test_img_names, test_cls))

那么训练集和验证集呢,一般我们是从训练数据中拿出一小份作为验证集,剩下的作为训练集,所以可以如下处理,首先读取到训练数据,并将其随机打乱:

images, labels, img_names, cls = self.load_data(self.train_path)
images, labels, img_names, cls = shuffle(images, labels, img_names, cls) # python函数,随机排序

然后以一定的比例划分成两份:

if isinstance(validation_size, float):
    validation_size = int(validation_size * images.shape[0])
# 验证集部分
validation_images = images[:validation_size]
validation_labels = labels[:validation_size]
validation_img_names = img_names[:validation_size]
validation_cls = cls[:validation_size]
# 测试集部分
train_images = images[validation_size:]
train_labels = labels[validation_size:]
train_img_names = img_names[validation_size:]
train_cls = cls[validation_size:]

注意,这里的validation_size输入的是一个0-1的小数,例如0.2,这样经过和images的总数处理就可以得到要一个比例分配后的数字。后面就是用这部分数据生成data_sets的另外两部分:

data_sets.set_train(DataSet(train_images,train_labels,train_img_names,train_cls))
data_sets.set_valid(DataSet(validation_images,validation_labels,validation_img_names,validation_cls))

此时我们的data_sets就全部获得了,这就是我们最终得到的数据对象。

(4)设计一个可视化程序

以上3个小节基本已经对数据完成了预处理操作,但是为了检验一些我们的图片数据,我们这里设计了一个可视化图像数据的程序,其实就是一个方法,这个方法的思路非常简单,就是从图像中随机抽取9张图片数据来展示:

# 随机采样9张图像
random_indices = random.sample(range(len(images)), min(len(images), 9))
images, cls_true = zip(*[(images[i], cls_true[i]) for i in random_indices])
# 创造一个3行3列的画布
fig, axes = plt.subplots(3, 3)
fig.subplots_adjust(hspace=0.6, wspace=0.6)
fig.canvas.set_window_title('Random Images show')#设置字体大小
for i, ax in enumerate(axes.flat):
    # 显示图片
    if len(images) < i + 1:
        break
    ax.imshow(images[i].reshape(img_size, img_size, num_channels))
    # 展示图像的语义标签和实际预测标签
    if cls_pred is None:
        xlabel = "True: {0}".format(cls_true[i])
else:
        xlabel = "True: {0}, Pred: {1}".format(cls_true[i], cls_pred[i])
    # 设置每张图的标签为其xlabel.
    ax.set_xlabel(xlabel)
    # 设置图片刻度
    ax.set_xticks([0, img_size])
    ax.set_yticks([0, img_size])
plt.show()

至此,数据预处理部分就基本结束了。

猜你喜欢

转载自blog.csdn.net/qq_41959920/article/details/114415561