yymn

caffe学习(2): Cifar-100 tutorial

由于caffe官方和很多博客已经提供了mnistcifar10在caffe上测试的教程,这里就不再复现这些教程了,今天和大家分享一下如何在caffe下训练cifar100数据集

一.数据准备

CIFAR-10 and CIFAR-100 datasetsCifar100和cifar10类似,训练集数目是50000,测试集是10000,只是分为20个大类和100个小类。

首先我们下载CIFAR-100 python version,下载完之后解压,在cifar-100-Python下会出现:meta,test和train三个文件,他们都是python用cPickle封装的pickled对象

 

[python] view plain copy
 
  1. def unpickle(file):  
  2.     import cPickle  
  3.     fo = open(file, \'rb\')  
  4.     dict = cPickle.load(fo)  
  5.     fo.close()  
  6.     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:

[python] view plain copy
 
  1. import os  
  2. import cPickle  
  3.   
  4. import numpy as np  
  5. import sklearn  
  6. import sklearn.linear_model  
  7.   
  8. import lmdb  
  9. import caffe  
  10.   
  11. def unpickle(file):  
  12.     fo = open(file, \'rb\')  
  13.     dict = cPickle.load(fo)  
  14.     fo.close()  
  15. return dict  
  16.   
  17. #调用sklearn对数据进行shuffle操作  
  18. def shuffle_data(data, labels):  
  19.     data, _, labels, _ = sklearn.cross_validation.train_test_split(  
  20.         data, labels, test_size=0.0, random_state=42  
  21.     )  
  22. return data, labels  
  23.   
  24. def load_data(train_file):  
  25.     d = unpickle(train_file)  
  26.     data = d[\'data\']  
  27.     fine_labels = d[\'fine_labels\']  
  28.     length = len(d[\'fine_labels\'])  
  29.   
  30.     data, labels = shuffle_data(  
  31.         data,  
  32.         np.array(fine_labels)  
  33.     )  
  34.     return (  
  35.         data.reshape(length, 3, 32, 32),  
  36.         labels  
  37.     )  
  38.   
  39. if __name__==\'__main__\':  
  40.     cifar_python_directory = os.path.abspath(\'cifar-100-python\')  
  41.   
  42.     print(\'Converting...\')  
  43.     cifar_caffe_directory=os.path.abspath(\'cifar100_train_lmdb\')  
  44. if not os.path.exists(cifar_caffe_directory):  
  45.   
  46.         X,y_f=load_data(os.path.join(cifar_python_directory, \'train\'))  
  47.         Xt,yt_f=load_data(os.path.join(cifar_python_directory, \'test\'))  
  48.   
  49. print(\'Data is fully loaded,now truly convertung.\')  
  50.           
  51.         #lmdb操作,将数据写入数据库  
  52.         env=lmdb.open(cifar_caffe_directory,map_size=50000*1000*5)  
  53.         txn=env.begin(write=True)  
  54.         count=0  
  55.         for i in range(X.shape[0]):  
  56.             datum=caffe.io.array_to_datum(X[i],y_f[i])  
  57.             str_id=\'{:08}\'.format(count)  
  58.             txn.put(str_id,datum.SerializeToString())  
  59.   
  60.             count+=1  
  61.             if count%1000==0:  
  62. print(\'already handled with {} pictures\'.format(count))  
  63.                 txn.commit()  
  64.                 txn=env.begin(write=True)  
  65.   
  66.         txn.commit()  
  67.         env.close()  
  68.   
  69.         env=lmdb.open(\'cifar100_test_lmdb\',map_size=10000*1000*5)  
  70.         txn=env.begin(write=True)  
  71.         count=0  
  72.         for i in range(Xt.shape[0]):  
  73.             datum=caffe.io.array_to_datum(Xt[i],yt_f[i])  
  74.             str_id=\'{:08}\'.format(count)  
  75.             txn.put(str_id,datum.SerializeToString())  
  76.   
  77.             count+=1  
  78.             if count%1000==0:  
  79. print(\'already handled with {} pictures\'.format(count))  
  80.                 txn.commit()  
  81.                 txn=env.begin(write=True)  
  82.   
  83.         txn.commit()  
  84.         env.close()  
  85. else:  
  86. print(\'Conversion was already done. Did not convert twice.\')  

ok,这样数据就被我们转换成熟悉的cifar100_train_lmdbcifar100_test_lmdb了,大家可以拿去做训练了



二.探秘LMDB

这部分我们来看看LMDB数据库中数据,并将其转化为可视化的png格式,直接上代码:

[python] view plain copy
 
  1. import lmdb  
  2. import os  
  3. import cv2  
  4. import cPickle  
  5. import caffe  
  6. from caffe.proto import caffe_pb2  
  7.   
  8. def unpickle(file):  
  9.     fo = open(file, \'rb\')  
  10.     dict = cPickle.load(fo)  
  11.     fo.close()  
  12. return dict  
  13.   
  14. if __name__==\'__main__\':  
  15.     #得到label具体对应的种类  
  16.     meta=unpickle(os.path.join(\'cifar-100-python\', \'meta\'))  
  17.     fine_label_names=meta[\'fine_label_names\']  
  18.   
  19.     env=lmdb.open(\'cifar100_train_lmdb\')  
  20.     txn=env.begin()  
  21.     cursor=txn.cursor()  
  22.     datum=caffe_pb2.Datum()  
  23.   
  24.     i=0  
  25.     for key,value in cursor:  
  26.         datum.ParseFromString(value)  
  27. if i<10:  
  28.             data=caffe.io.datum_to_array(datum)  
  29.             label=datum.label  
  30.             img=data.transpose(1,2,0)  
  31.             #图片名字为其类别  
  32.             cv2.imwrite(\'{}.png\'.format(fine_label_names[label]),img)  
  33.         i+=1  
  34.   
  35.     env.close()  
  36. print(\'there are totally {} pictures\'.format(i))  
运行一下之后,本地目录下就会出现训练集中的前十张图片,打开看一下,由于分辨率只有32x32,所以图像很不清晰,这里就不贴了。这个script也可以应用到其他的lmdb数据库中,大家可以看一下mnist中的数据,也可以看一下ImageNet的数据,这里贴一张我在ilsvrc12_val_lmdb中转换出来的一张图片:

三.Data Augmentation

其实做完第一步,得到的数据已经可以用来进行训练运行了,但是这里我们实验对比一下data augmentation的强大之处。常用的data augmentation有horizontally flippingrandom crops and color jittering.


在Cifar数据集上比较常用的是前两种,这里我们做一组对比实验:

1.训练时采取random crop,crop出28X28的进行训练,再结合flip horizontally

2.对比实验,不采用data augmentation

其中random cropflip horizontally在caffe中通过指定参数即可



四.Data Pre-processing

对图像的数据预处理通常都是采取减去像素均值的方式,caffe是自带compute_image_mean的脚本的,不过这里还是给出python版本的:

[python] view plain copy
 
  1. import caffe  
  2. import lmdb  
  3. import numpy as np  
  4. from caffe.proto import caffe_pb2  
  5. import time  
  6.   
  7. lmdb_env=lmdb.open(\'cifar100_train_lmdb\')  
  8. lmdb_txn=lmdb_env.begin()  
  9. lmdb_cursor=lmdb_txn.cursor()  
  10. datum=caffe_pb2.Datum()  
  11.   
  12. N=0  
  13. mean = np.zeros((1, 3, 32, 32))  
  14. beginTime = time.time()  
  15. for key,value in lmdb_cursor:  
  16.     datum.ParseFromString(value)  
  17.     data=caffe.io.datum_to_array(datum)  
  18.     image=data.transpose(1,2,0)  
  19.     mean[0,0] += image[:, :, 0]  
  20.     mean[0,1] += image[:, :, 1]  
  21.     mean[0,2] += image[:, :, 2]  
  22.     N+=1  
  23.     if N % 1000 == 0:  
  24.         elapsed = time.time() - beginTime  
  25. print("Processed {} images in {:.2f} seconds. "  
  26.               "{:.2f} images/second.".format(N, elapsed,  
  27.                                              N / elapsed))  
  28. mean[0]/=N  
  29. blob = caffe.io.array_to_blobproto(mean)  
  30. with open(\'mean.binaryproto\', \'wb\') as f:  
  31.     f.write(blob.SerializeToString())  
  32.   
  33. 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,并允许水平翻转,样本数量相当于增加到((32-28)^2)*2=32,这可以大大抑制模型的过拟合,增加模型的泛化能力。

最后,贴一下github链接:GitHub - fish145/uncommon-datasets-caffe,实验中的scripts和实验配置文件均已经分享在其中!

分类:

技术点:

相关文章: