caffe学习(2): Cifar-100 tutorial
一.数据准备
CIFAR-10 and CIFAR-100 datasetsCifar100和cifar10类似,训练集数目是50000,测试集是10000,只是分为20个大类和100个小类。
首先我们下载CIFAR-100 python version,下载完之后解压,在cifar-100-Python下会出现:meta,test和train三个文件,他们都是python用cPickle封装的pickled对象
- def unpickle(file):
- import cPickle
- fo = open(file, \'rb\')
- dict = cPickle.load(fo)
- fo.close()
- return dict
通过以上代码可以将其转换成一个dict对象,test和train的dict中包含以下元素:
data——一个nx3072的numpy数组,每一行都是(32,32,3)的RGB图像,n代表图像个数
coarse_labels——一个范围在0-19的包含n个元素的列表,对应图像的大类别
fine_labels——一个范围在0-99的包含n个元素的列表,对应图像的小类别
而meta的dict中只包含fine_label_names,第i个元素对应其真正的类别。
但是caffe不支持这样的数据格式啊,下面我们用一段python脚本将其转换为大家熟悉的lmdb:
- import os
- import cPickle
- import numpy as np
- import sklearn
- import sklearn.linear_model
- import lmdb
- import caffe
- def unpickle(file):
- fo = open(file, \'rb\')
- dict = cPickle.load(fo)
- fo.close()
- return dict
- #调用sklearn对数据进行shuffle操作
- def shuffle_data(data, labels):
- data, _, labels, _ = sklearn.cross_validation.train_test_split(
- data, labels, test_size=0.0, random_state=42
- )
- return data, labels
- def load_data(train_file):
- d = unpickle(train_file)
- data = d[\'data\']
- fine_labels = d[\'fine_labels\']
- length = len(d[\'fine_labels\'])
- data, labels = shuffle_data(
- data,
- np.array(fine_labels)
- )
- return (
- data.reshape(length, 3, 32, 32),
- labels
- )
- if __name__==\'__main__\':
- cifar_python_directory = os.path.abspath(\'cifar-100-python\')
- print(\'Converting...\')
- cifar_caffe_directory=os.path.abspath(\'cifar100_train_lmdb\')
- if not os.path.exists(cifar_caffe_directory):
- X,y_f=load_data(os.path.join(cifar_python_directory, \'train\'))
- Xt,yt_f=load_data(os.path.join(cifar_python_directory, \'test\'))
- print(\'Data is fully loaded,now truly convertung.\')
- #lmdb操作,将数据写入数据库
- env=lmdb.open(cifar_caffe_directory,map_size=50000*1000*5)
- txn=env.begin(write=True)
- count=0
- for i in range(X.shape[0]):
- datum=caffe.io.array_to_datum(X[i],y_f[i])
- str_id=\'{:08}\'.format(count)
- txn.put(str_id,datum.SerializeToString())
- count+=1
- if count%1000==0:
- print(\'already handled with {} pictures\'.format(count))
- txn.commit()
- txn=env.begin(write=True)
- txn.commit()
- env.close()
- env=lmdb.open(\'cifar100_test_lmdb\',map_size=10000*1000*5)
- txn=env.begin(write=True)
- count=0
- for i in range(Xt.shape[0]):
- datum=caffe.io.array_to_datum(Xt[i],yt_f[i])
- str_id=\'{:08}\'.format(count)
- txn.put(str_id,datum.SerializeToString())
- count+=1
- if count%1000==0:
- print(\'already handled with {} pictures\'.format(count))
- txn.commit()
- txn=env.begin(write=True)
- txn.commit()
- env.close()
- else:
- print(\'Conversion was already done. Did not convert twice.\')
ok,这样数据就被我们转换成熟悉的cifar100_train_lmdb和cifar100_test_lmdb了,大家可以拿去做训练了
二.探秘LMDB
这部分我们来看看LMDB数据库中数据,并将其转化为可视化的png格式,直接上代码:
- import lmdb
- import os
- import cv2
- import cPickle
- import caffe
- from caffe.proto import caffe_pb2
- def unpickle(file):
- fo = open(file, \'rb\')
- dict = cPickle.load(fo)
- fo.close()
- return dict
- if __name__==\'__main__\':
- #得到label具体对应的种类
- meta=unpickle(os.path.join(\'cifar-100-python\', \'meta\'))
- fine_label_names=meta[\'fine_label_names\']
- env=lmdb.open(\'cifar100_train_lmdb\')
- txn=env.begin()
- cursor=txn.cursor()
- datum=caffe_pb2.Datum()
- i=0
- for key,value in cursor:
- datum.ParseFromString(value)
- if i<10:
- data=caffe.io.datum_to_array(datum)
- label=datum.label
- img=data.transpose(1,2,0)
- #图片名字为其类别
- cv2.imwrite(\'{}.png\'.format(fine_label_names[label]),img)
- i+=1
- env.close()
- print(\'there are totally {} pictures\'.format(i))
三.Data Augmentation
其实做完第一步,得到的数据已经可以用来进行训练运行了,但是这里我们实验对比一下data augmentation的强大之处。常用的data augmentation有horizontally flipping, random crops and color jittering.
在Cifar数据集上比较常用的是前两种,这里我们做一组对比实验:
1.训练时采取random crop,crop出28X28的进行训练,再结合flip horizontally
2.对比实验,不采用data augmentation
其中random crop和flip horizontally在caffe中通过指定参数即可
四.Data Pre-processing
对图像的数据预处理通常都是采取减去像素均值的方式,caffe是自带compute_image_mean的脚本的,不过这里还是给出python版本的:
- import caffe
- import lmdb
- import numpy as np
- from caffe.proto import caffe_pb2
- import time
- lmdb_env=lmdb.open(\'cifar100_train_lmdb\')
- lmdb_txn=lmdb_env.begin()
- lmdb_cursor=lmdb_txn.cursor()
- datum=caffe_pb2.Datum()
- N=0
- mean = np.zeros((1, 3, 32, 32))
- beginTime = time.time()
- for key,value in lmdb_cursor:
- datum.ParseFromString(value)
- data=caffe.io.datum_to_array(datum)
- image=data.transpose(1,2,0)
- mean[0,0] += image[:, :, 0]
- mean[0,1] += image[:, :, 1]
- mean[0,2] += image[:, :, 2]
- N+=1
- if N % 1000 == 0:
- elapsed = time.time() - beginTime
- print("Processed {} images in {:.2f} seconds. "
- "{:.2f} images/second.".format(N, elapsed,
- N / elapsed))
- mean[0]/=N
- blob = caffe.io.array_to_blobproto(mean)
- with open(\'mean.binaryproto\', \'wb\') as f:
- f.write(blob.SerializeToString())
- lmdb_env.close()
运行之后就可以得到mean.binaryproto了
五.对比实验
前面的准备工作终于完成了,下面我们进行对比实验.
采用的网络结构在这里:ethereon,这是我自己设计的一个简单结构:
包含三个卷积层,一个池化层以及一个全连接层.
首先我们不采用data augmentation,进行实验:
实验结果如下:
最终在测试集上的测试结果为46.89%
采用data augmentation:
实验结果如下:
最终在测试集上的结果为49.97%
再来比较一下训练的过程:
可以看到,采用了data augmentation后,performance提升了超过3%,可以看出这个trick对于深度学习的重要性.
其原理也很容易解释,众所周知,深度学习目前成功的一个重要因素就是数以百万计带标记的数据样本的出现。换言之,深度学习的高performance一定程度上决定于目前的海量数据,而data augmentation技术相当于增加了数据样本数量
拿本例来说,32x32的图片随机crop到28x28,并允许水平翻转,样本数量相当于增加到,这可以大大抑制模型的过拟合,增加模型的泛化能力。
最后,贴一下github链接:GitHub - fish145/uncommon-datasets-caffe,实验中的scripts和实验配置文件均已经分享在其中!