注意:这是一个完整的项目,建议您按照完整的博客顺序阅读。
目录
一、对数据集进行预处理
(1)设计一个预处理程序
(2)设计一个数据存储对象
(3)设计一个转换程序
(4)设计一个可视化程序
在第一步,我们要对数据集进行一些处理,主要任务是“将数据集文件处理成可读取到内存进行特征学习的数据对象(DataSets)”。
我们分析该任务,可以归纳出该阶段工作的主要步骤:
步骤 |
主要任务 |
(1) |
设计一个预处理程序,将图片读取到内存并进行缩放等预处理。 |
(2) |
设计一个数据存储对象,用来存储训练集、验证集和测试集。 |
(3) |
设计一个转换程序,用来将内存数据转换成统一的数据对象。 |
(4) |
设计一个可视化程序,用来可视化预处理后的图片数据,验证数据处理是否合适(附加) |
现在假设我们已经有了一份数据集文件,其文件结构是:
我们的目标是将这份文件读取到内存中,同时保留它的图片数据信息、图片标签信息、图片标签语义信息、图片名称信息等。
现在先考虑将文件读取到内存中,要读取文件先得知道文件所在路径,所以我们第一步是得到一个图片文件路径的列表。我们先来看下图片文件的路径特征
'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之间。
读取到的所有图像数据,我们将会存储到一个数据对象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]
在前面两节我们定义了图片的预处理程序以及我们最终要生成的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就全部获得了,这就是我们最终得到的数据对象。
以上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()
至此,数据预处理部分就基本结束了。