手把手教你CIFAR数据集可视化
CIFAR数据集介绍与获取
如同从小到的父母教我们识别每个物体是什么一样,除了看到的画面,父母会在旁边告诉看到的画面是什么,这种学习方式叫做监督学习(与此对应还有无监督学习)。计算机也一样。数据集通常应该至少包含两部分内容,一个是图像,一个是该图像对应的物体是什么。
CIFAR-10是一个流行的小级别(与其他数据集相比,例如ImageNet,无论物体个数、单张图像的尺寸以及图像的数量都相对较小)的图像分类数据集。这个数据集由60000个高宽都为32像素的微小图像组成。每个图像都用10个类别中的一个来标记(飞机、汽车、鸟...)。这60000张图片被分成50000张图片的训练集和10000张图片的测试集。在图1中,可以看到10个类中每一个类的10个随机示例图像:
图1 CIFAR数据示例
通过访问CIFAR官网 http://www.cs.toronto.edu/~kriz/cifar.html 即可下载该数据集,如图2所示,选择“CIFAR-10 python version”进行下载;Python已经成为人工智能领域应用最广泛的语言,所以,本章节的所有算法实现皆采用Python进行实现。
图2 CIFAR官网下载部分截图
下载完成后,解压数据包,可以看到有以下文件,如图3所示。
图3 CIAR-10 python version解压后文件
前面讲过,CIAR包含60000张图片,被分成50000张图片的训练集和10000张图片的测试集。
50000张训练集图片分别存于“data_batch_1”、“data_batch_2”、“data_batch_3”、“data_batch_4”、“data_batch_5”中,10000张测试图像存于“test_batch”中。
“batches.meta”则存储了一个字典(请查阅相关资料学习Python的列表、字典和元祖三种常用数据结构/数据类型;在本书中,数据结构指数据对象中数据元素之间的关系;也就是字典被认为是数据结构,而实例化的一个字典被认为是数据对象),稍后通过程序将该字典输出。
“readme.html”双击通过浏览器打开是CIFAR的官方网站,里面有CIFAR-10的下载链接和对于CIFAR-10的介绍和说明。
不过有些同学可能会疑惑,在这些文件里面没有看到一张图像,这60000张图像哪里来的?
接下来,通过Python编程将数据集内的部分图像可视化;一方面,捎带讲解Python的语法特点和相关常用模块,另一方面,可以直观看一看该数据集里面的图像。
open()函数、pickle模块以及with...as...的使用
首先看一看“batches.meta”存储的字典的内容,这里要用到Python的open()函数、pickle模块以及with...as语法,这里简单解释一下:
想把数据写入在文件中,首先需要open()函数进行打开文件(如果该文件不存在则自动先创建文件),然后通过write()函数进行写入。但是write()函数只支持写入字符串数据类型,而列表、字典直接使用write()写入将不被允许,如程序1所示。
程序1 使用open()以及write()写入数据
a = 'helloVision!'
f = open('E:/testOpen.txt','w')
f.write(a)
f.close()
#以下是错误示例,write()只能写入字符串,其他数据类型如列表、字典等不允许写入
"""
a = [12,34,56]
f = open('E:/testOpen.txt','w')
f.write(a)
f.close()
"""
程序将字符串”helloVision”写入到文件中,其中open()函数的第二个参数’w’表示清空文件内容后写入,如果想不清空文件内容并追加写入则用’a’。
三个双引号能够实现多行注释,并以三个双引号结束注释;如果取消注释,将错误示例进行运行,则报出以下错误,如图4所示:
图4 写入列表数据类型报错
那么,当我们使用Python想把一些数据——例如字符串、列表、字典等,保存在电脑中(或者从电脑里读出保存在电脑里的Python数据内容),怎么办?
这就要用到pickle模块。该模块有两个常用函数——dump()和load(),简单来说,dump()函数将Python的数据对象通过特殊的形式转换为只有python语言认识的字符串,而load()函数则将转换后的字符串转换回Python数据对象,前者成为序列化,后者成为反序列化。在进行序列化之后,就可以通过write()进行写入。如程序2所示。
程序2 将列表写入文件中
import pickle
b = [12,34,56]
f3 = open('E:/listWrite.txt','wb')
pickle.dump(b,f3)
f3.close()
第1行输入pickle模块,这里有点类似于C/C++语言里面的#include;这样就可以在下面使用pickle模块的功能。
在使用pickle模块下的dump()和load()在进行写入读出之前,无论是读数据还是写数据,依然都需要先打开文件,所以需要先使用打开文件的open()函数,需要注意的是这里open()函数的第二个参数为’wb’,而不是’w’。’w’是以文本进行写入,而’wb’则是以二进制进行写入;两者有区别。可自行搜索文本与二进制方式打开文件的区别补充相关知识。
写入后,就可以将写入的列表读出来,如程序3所示。
程序3 将存储列表的文件内容读出
import pickle
f3 = open('E:/listWrite.txt','rb')
b = pickle.load(f3)
print(b)
f3.close()
注意第二行open()函数的第二个参数是’rb’,以二进制形式打开文件
但是,在打开文件的时候,有可能发生文件读取数据异常,或者打开后忘记关闭文件。在Python中,可以使用try...except...finally....来处理异常;除此之外,with...as...也可以用来处理该问题,with...as...不仅语法更简洁,还可以更好的处理上下文环境产生的异常(具体的with...as...语法原理和使用可查阅相关资料)。程序3则可以写成如下形式。
程序4 使用with...as...打开文件
import pickle
with open('E:/listWrite.txt','rb') as f3:
b = pickle.load(f3)
print(b)
显示batches.meta的内容
有了以上的准备工作,就可以在进行CIFAR部分数据可视化之前,先看一看batches.meta文件中的内容了。如程序5所示。
程序5 打印batches.meta的内容
import pickle
def load_file(filename):
with open(filename, 'rb') as fo:
data = pickle.load(fo, encoding='latin1')
return data
data = load_file('E:/cifar/cifar-10-batches-py/batches.meta')
print(data.keys())
print(data.values())
在第3行至第6行,实现了一个名为load_file的函数,该函数通过传入一个存有Python数据对象的文件路径,最终返回该数据对象。第4行通过with...as...以及open()函数将文件打开,open函数的第一个参数比较简单,很容易看出是文件的路径;第二个参数是打开的方式,‘rb’是二进制读取(除此之外还有其他一些打开方式,可自行学习)。打开后,就可以使用pickle的load()函数对文件内容进行读入;最后,返回读取到的内容。
latin1是ISO-8859-1的别名,向下兼容ASCII码,这里将’latin1’换成’ASCII’码也可以。
第8行调用load_file读取存在”E:/cifar/cifar-10-batches-py”文件夹下面的batches.meta文件,并将该文件存储的Python数据对象赋值给data。
因为data是一个字典,字典内包含键和值两部分,keys()和values()两个函数能够返回该字典的键和值。第9行与第10行分别输出data的键与值。如图5下所示:
图5 batches.meta的内容显示
这里需要注意的是图5中dict_values的['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck'],后面 data_batch_1, data_batch_2, ..., data_batch_5文件中的图片中的目标物体标签为0至9,0-9按顺序对应该列表的物体'airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck’。
CIFAR数据集部分图像可视化
这一小节将CIFAR数据集的一部分图像进行可视化,也就是打开数据集,将里面的图像数据读取出来保存为常用的图像格式,如png格式。
这时先看一下CIFAR官网对于 data_batch_1, data_batch_2, ..., data_batch_5文件的介绍。如图6所示。
图6 CIFAR官网Dataset layout部分截图
该部分内容提供了unpickle()函数,通过该函数能够对数据集文件进行读取,并返回数据集文件中存储的字典;并对该字典的data和labels元素内容进行了详细说明。
先写一个程序看一看该字典的键值内容,如程序6所示。
程序6 打印data_batch_1的键和值
# 读取文件
def unpickle(file):
import pickle
with open(file, 'rb') as fo:
dict = pickle.load(fo, encoding='bytes')
return dict
x = unpickle("E:/cifar/cifar-10-batches-py/data_batch_1")
print(x.keys())
print(x.values())
运行以上程序,可以看到该字典的键和值(分别用红框圈出),如图7所示。
图7 打印data_batch_1的键和值
其中比较重要的是键b'labels'、键b'data',以及对应的值。因为b'data'对应的是图像数据,而b'labels'对应的是图像的标签。这里解释一下b'labels'、b'data'...因为pickle.load()是以bytes形式进行读取的,所以这里的键并非是字符串(字符串使用单引号,而bytes对象则是b’’),而是bytes对象。
那么,我们关心的图像和对应的标签是如何存储的?
首先,一副尺寸为32*32的彩色图像需要多少个数值?32行32列共计32*32个像素,即1024个像素,每个像素需要三个数据来存储红Red、绿Green、蓝Blue对应的数值,所以共需32*32*3=3072。
可以看到图6中对于data的解释——a 10000x3072 numpy array of uint8s。即一个10000行3072列的numpy数组,每个元素为uint8类型。而numpy则是Python中处理数组(也可以认为是线性代数中的矩阵)的强大工具,后面会大量用到。可以联想到,每行代表一副图像,共一万行,即一万张;每行3072个元素,即每幅32*32彩色图像对应的RGB颜色数值。
接下来,终于可以将CIFAR数据集的部分图像进行可视化;如程序7所示,该程序将data_batch_1文件中存储图像的numpy数组的前200行转换为相应的200副图像文件,并分别放在以对应标签命名的文件夹中。
程序7 CIFAR数据集图像可视化
import pickle
import os
from scipy.misc import imsave
import numpy as np
def load_file(filename):
with open(filename, 'rb') as fo:
data = pickle.load(fo, encoding='latin1')
return data
dic = load_file('E:/cifar/cifar-10-batches-py/batches.meta')
labels_item = dic['label_names']
#创建文件夹
for i in range(10):
if not os.path.exists("E:/images/" + labels_item[i]):
os.makedirs("E:/images/" + labels_item[i])
def unpickle(file):
with open(file, 'rb') as fo:
dict = pickle.load(fo, encoding='bytes')
return dict
dic = unpickle("E:/cifar/cifar-10-batches-py/data_batch_1")
dict_image_data = dic[b'data']
dict_image_labels = dic[b'labels']
for i in range(200):
imgs = dict_image_data[i]
labels = dict_image_labels[i]
imgs_array = np.reshape(imgs,(3,32,32))
imgs_array = imgs_array.transpose(1, 2, 0)
imsave("E:/images/" + labels_item[labels] + "/" + str(i) + '.jpg', imgs_array)
#RGB图像的数据存储顺序
img1 = dict_image_data[0]
print("data_batch1中以单行形式存储的第一个幅RGB图像数据:")
print(img1)
print("将该行转换成3*32*32的数组形式:")
img1_array = np.reshape(img1,(3,32,32))
print(img1_array)
print("将每个像素的RGB值放在一起")
img2_array = img1_array.transpose(1, 2, 0)
print(img2_array)
这个程序需要用到四个模块,分别是pickle、os、numpy以及scipy,其中pickle已经接触过了;os在后面用来在计算机中创建文件夹;numpy前面提起过,用来做数组处理;scipy的模块misc中有imsave()函数,用来将读取到的图像信息保存为常见的图像格式。
代码from scipy.misc import imsave表示从scipy.misc模块中使用imsave()函数,后面就可以直接使用imsave()函数,如果程序比较大,存在命名冲突的可能性增加,则不建议这么写。可以写成import scipy.misc,后面在使用imsave()函数的时候,前面需要加限定:scipy.misc.imsave(),即 scipy.misc模块下的imsave()。
而import numpy as np则表示使用名称np替代名称numpy。
第6至9行前面已经讲过,是为了读取batches.meta而写的函数;11行调用该函数完成读取,并文件内的字典赋值给dic;接下来将该字典的键'label_names'对应的值(该值是一个列表)赋值给列表labels_item,这个值在前面也已经讲过,即:['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck’]。
读取这个值是为了创建10个文件夹,分别为airplane, automobile, bird, cat, deer, dog, frog, horse, ship, truck;后面将图像按照标签分别存放在对应的文件夹里面。
15行至17行实现了10个文件夹的创建。全部程序运行后,可以看到E盘有了一个images文件夹,该文件夹中有十个文件夹,如图8所示。
8 文件夹创建结果
19行至22行前面讲过,为了读取data_batches文件实现了unpickle函数。
24行,调用函数并将文件中的字典赋值给dic;25行将dic字典的b’data’对应的值赋值给dict_image_data;26行将b'labels'对应的值赋值给dict_image_labels。这里再次提示:dict_image_data的每一行表示一副图像,dict_image_labels则按照顺序对应dict_image_data的每一行表示图像的标签。
28至33行, dict_image_data的第i行数据赋值给imgs ,而dict_image_labels将第i个标签赋值给labels,这个时候还不能直接将imgs保存为图像,31、32行代码数据进行了整理(稍后讲解如何整理的),以便在33行的imsave()函数进行保存。Imsave()函数第一个参数为保存路径(含该文件的名称),第二个参数为保存的数组变量(因为名称的后缀为jpg,Python自动识别后按照该文件类型的格式进行了保存)。完整程序运行后,可以看到airplane文件夹所保存的图像,如图9所示。
图9 airplace文件夹中图像示例
38至47行代码打印显示出了“整理数据”的过程。
36行将第一幅图像的数据复制给了img1;然后打印了data_batch1的第一行数据,即img1如图10所示。
图5-2-10 data_batch1的第一行数据print结果
这里解释一下这一行的数据,CIFAR的数据按照RGB三色依次存储,也就是说,一幅图像虽然每个像素点分RGB三个值,但是CIFAR的数据一次性存储完R后,再存G,再存B。32*32的彩色图像,存储R值需要32*32=1024个值,也就是img1的前1024个数表示第一幅图像的R值;其后1024个值表示G值;最后1024个数值表示B值。
42行将图像转置为3*32*32的数组形式,如图11所示。
图11 CIFAR第一幅图像的3*32*32数组表示
注意图11中红框圈出的三个数值59,62,63,这三个数值分别表示第一个像素点的RGB三个值。但是,图像的保存需要的数组每个像素的RGB值应该是在一起排列的,所以按照第46行代码进行数组的转置。此时,数组的排列满足了图像存储的要求,如图12所示,打印该数组,可以看到第1个像素的RGB依次排列,第2,3...像素也按照该次序进行排列。
图12 符合图像保存的数组格式
下一篇: 透明对话框
推荐阅读
-
手把手教你用Node.js爬虫爬取网站数据的方法
-
手把手教你如何用Fiddler抓取手机数据包(iOS+Android)
-
Python数据分析:手把手教你用Pandas生成可视化图表的教程
-
Python可视化查看数据集完整性: missingno库(用于数据分析前的数据检查)
-
网络营销seo 手把手教你做企业网站数据库营销
-
KNN实现CIFAR-10数据集识别
-
分别采用线性LDA、k-means和SVM算法对鸢尾花数据集和月亮数据集进行二分类可视化分析
-
COCO数据集进行自定义可视化,不用cocoapi
-
从数据到决策,手把手教你B2C电商数据分析怎么做
-
PyTorch读取Cifar数据集并显示图片的实例讲解