深度學習(十三)caffe之訓練數據格式

原文地址http://blog.csdn.net/hjimce/article/details/49248231

作者:hjimce

caffe對於訓練數據格式,支持:lmdb、h5py……,其中lmdb數據格式常用於單標籤數據,像分類等,經常使用lmdb的數據格式。對於迴歸等問題,或者多標籤數據,一般使用h5py數據的格式。當然好像還有其它格式的數據可用,不過我一般使用這兩種數據格式,因此本文就主要針對這兩種數據格式的製作方法,進行簡單講解。

一、lmdb數據

lmdb用於單標籤數據。爲了簡單起見,我後面通過一個性別分類作爲例子,進行相關數據製作講解。

1、數據準備

首先我們要準備好訓練數據,然後新建一個名爲train的文件夾和一個val的文件夾:


train文件存放訓練數據,val文件存放驗證數據。然後我們在train文件下面,把訓練數據性別爲男、女圖片各放在一個文件夾下面:


同樣的我們在val文件下面也創建文件夾:


兩個文件也是分別存我們用於驗證的圖片數據男女性別文件。我們在test_female下面存放了都是女性的圖片,然後在test_male下面存放的都是驗證數據的男性圖片。

2、標籤文件.txt文件製作.

接着我們需要製作一個train.txt、val.txt文件,這兩個文件分別包含了我們上面的訓練數據的圖片路徑,以及其對應的標籤,如下所示。


我們把女生圖片標號爲1,男生圖片標記爲0。標籤數據文件txt的生成可以通過如下代碼,通過掃描路徑男、女性別下面的圖片,得到標籤文件train.txt和val.txt:

[python] view plain copy
  1. <span style="font-family:Arial;font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">import os  
  2. import numpy as np  
  3. from matplotlib import pyplot as plt  
  4. import cv2  
  5. import shutil  
  6.   
  7.   
  8. #掃面文件  
  9. def GetFileList(FindPath,FlagStr=[]):        
  10.     import os  
  11.     FileList=[]  
  12.     FileNames=os.listdir(FindPath)  
  13.     if len(FileNames)>0:  
  14.         for fn in FileNames:  
  15.             if len(FlagStr)>0:  
  16.                 if IsSubString(FlagStr,fn):  
  17.                     fullfilename=os.path.join(FindPath,fn)  
  18.                     FileList.append(fullfilename)  
  19.             else:  
  20.                 fullfilename=os.path.join(FindPath,fn)  
  21.                 FileList.append(fullfilename)  
  22.   
  23.      
  24.     if len(FileList)>0:  
  25.         FileList.sort()  
  26.   
  27.     return FileList  
  28. def IsSubString(SubStrList,Str):        
  29.     flag=True  
  30.     for substr in SubStrList:  
  31.         if not(substr in Str):  
  32.             flag=False  
  33.   
  34.     return flag  
  35.   
  36. txt=open('train.txt','w')  
  37. #製作標籤數據,如果是男的,標籤設置爲0,如果是女的標籤爲1  
  38. imgfile=GetFileList('first_batch/train_female')  
  39. for img in imgfile:  
  40.     str=img+'\t'+'1'+'\n'  
  41.     txt.writelines(str)  
  42.   
  43. imgfile=GetFileList('first_batch/train_male')  
  44. for img in imgfile:  
  45.     str=img+'\t'+'0'+'\n'  
  46.     txt.writelines(str)  
  47. txt.close()</span></span></span>  

把生成的標籤文件,和train\val文件夾放在同一個目錄下面:


需要注意,我們標籤數據文件裏的文件路徑和圖片的路徑要對應的起來,比如val.txt文件的某一行的圖片路徑,是否在val文件夾下面:


3、生成lmdb數據

接着我們的目的就是要通過上面的四個文件,把圖片的數據和其對應的標籤打包起來,打包成lmdb數據格式,打包腳本如下:

[python] view plain copy
  1. <span style="font-family:Arial;font-size:18px;"><span style="font-size:18px;">#!/usr/bin/env sh  
  2. # Create the imagenet lmdb inputs  
  3. # N.B. set the path to the imagenet train + val data dirs  
  4.   
  5. EXAMPLE=.          # 生成模型訓練數據文化夾  
  6. TOOLS=//../build/tools                              # caffe的工具庫,不用變  
  7. DATA=.                  # python腳步處理後數據路徑  
  8.   
  9. TRAIN_DATA_ROOT=train/  #待處理的訓練數據  
  10. VAL_DATA_ROOT=val/      # 帶處理的驗證數據  
  11.   
  12.   
  13.   
  14. # Set RESIZE=true to resize the images to 256x256. Leave as false if images have  
  15. # already been resized using another tool.  
  16. RESIZE=true#是否需要對圖片進行resize  
  17. if $RESIZE; then  
  18.   RESIZE_HEIGHT=256  
  19.   RESIZE_WIDTH=256  
  20. else  
  21.   RESIZE_HEIGHT=0  
  22.   RESIZE_WIDTH=0  
  23. fi  
  24.   
  25. if [ ! -d "$TRAIN_DATA_ROOT" ]; then  
  26.   echo "Error: TRAIN_DATA_ROOT is not a path to a directory: $TRAIN_DATA_ROOT"  
  27.   echo "Set the TRAIN_DATA_ROOT variable in create_imagenet.sh to the path" \  
  28.        "where the ImageNet training data is stored."  
  29.   exit 1  
  30. fi  
  31.   
  32. if [ ! -d "$VAL_DATA_ROOT" ]; then  
  33.   echo "Error: VAL_DATA_ROOT is not a path to a directory: $VAL_DATA_ROOT"  
  34.   echo "Set the VAL_DATA_ROOT variable in create_imagenet.sh to the path" \  
  35.        "where the ImageNet validation data is stored."  
  36.   exit 1  
  37. fi  
  38.   
  39. echo "Creating train lmdb..."  
  40.   
  41. GLOG_logtostderr=1 $TOOLS/convert_imageset \  
  42.     --resize_height=$RESIZE_HEIGHT \  
  43.     --resize_width=$RESIZE_WIDTH \  
  44.     --shuffle \  
  45.     $TRAIN_DATA_ROOT \  
  46.     $DATA/train.txt \  
  47.     $EXAMPLE/train_lmdb  
  48.   
  49. echo "Creating val lmdb..."  
  50.   
  51. GLOG_logtostderr=1 $TOOLS/convert_imageset \  
  52.     --resize_height=$RESIZE_HEIGHT \  
  53.     --resize_width=$RESIZE_WIDTH \  
  54.     --shuffle \  
  55.     $VAL_DATA_ROOT \  
  56.     $DATA/val.txt \  
  57.     $EXAMPLE/val_lmdb  
  58.   
  59. echo "Done."</span></span>  
通過運行上面的腳本,我們即將得到文件夾train_lmdb\val_lmdb:

我們打開train_lmdb文件夾


並查看一下文件data.mdb數據的大小,如果這個數據包好了我們所有的訓練圖片數據,查一下這個文件的大小是否符合預期大小,如果文件的大小才幾k而已,那麼就代表你沒有打包成功,估計是因爲路徑設置錯誤。我們也可以通過如下的代碼讀取上面打包好的數據,把圖片、和標籤打印出來,查看一下,查看lmdb數據請參考下面的代碼:

Python lmdb數據驗證:

[python] view plain copy
  1. <span style="font-family:Arial;font-size:18px;"><span style="font-size:18px;"># coding=utf-8  
  2. caffe_root = '/home/hjimce/caffe/'  
  3. import sys  
  4. sys.path.insert(0, caffe_root + 'python')  
  5. import caffe  
  6.   
  7. import os  
  8. import lmdb  
  9. import numpy  
  10. import matplotlib.pyplot as plt  
  11.   
  12.   
  13. def readlmdb(path,visualize = False):  
  14.     env = lmdb.open(path, readonly=True,lock=False)  
  15.   
  16.     datum = caffe.proto.caffe_pb2.Datum()  
  17.     x=[]  
  18.     y=[]  
  19.     with env.begin() as txn:  
  20.         cur = txn.cursor()  
  21.         for key, value in cur:  
  22.             # 轉換爲datum  
  23.             datum.ParseFromString(value)  
  24.             # 讀取datum數據  
  25.             img_data = numpy.array(bytearray(datum.data))\  
  26.                 .reshape(datum.channels, datum.height, datum.width)  
  27.             print img_data.shape  
  28.             x.append(img_data)  
  29.             y.append(datum.label)  
  30.             if visualize:  
  31.                 img_data=img_data.transpose([1,2,0])  
  32.                 img_data = img_data[:, :, ::-1]  
  33.                 plt.imshow(img_data)  
  34.                 plt.show()  
  35.                 print datum.label  
  36.     return  x,y</span></span>  

通過上面的函數,我們可以是讀取相關的lmdb數據文件。

4、製作均值文件。

這個是爲了圖片歸一化而生成的圖片平均值文件,把所有的圖片相加起來,做平均,具體的腳本如下:

[python] view plain copy
  1. #!/usr/bin/env sh  
  2. # Compute the mean image from the imagenet training lmdb  
  3. # N.B. this is available in data/ilsvrc12  
  4.   
  5. EXAMPLE=.  
  6. DATA=train  
  7. TOOLS=../../build/tools   
  8.   
  9. $TOOLS/compute_image_mean $EXAMPLE/train_lmdb \  #train_lmdb是我們上面打包好的lmdb數據文件  
  10.   $DATA/imagenet_mean.binaryproto  
  11.   
  12. echo "Done."  

運行這個腳本,我們就可以訓練圖片均值文件:imagenet_mean.binaryproto

至此,我們得到了三個文件:imagenet_mean.binaryproto、train_lmdb、val_lmdb,這三個文件就是我們最後打包好的數據,這些數據我們即將作爲caffe的數據輸入數據格式文件,把這三個文件拷貝出來,就可以把原來還沒有打包好的數據刪了。這三個文件,我們在caffe的網絡結構文件,數據層定義輸入數據的時候,就會用到了:

[python] view plain copy
  1. name: "CaffeNet"  
  2. layers {  
  3.   name: "data"  
  4.   type: DATA  
  5.   top: "data"  
  6.   top: "label"  
  7.   data_param {  
  8.     source: "train_lmdb"#lmbd格式的訓練數據  
  9.     backend: LMDB  
  10.     batch_size: 50  
  11.   }  
  12.   transform_param {  
  13.     crop_size: 227  
  14.     mirror: true  
  15.     mean_file:"imagenet_mean.binaryproto"#均值文件  
  16.   
  17.   }  
  18.   include: { phase: TRAIN }  
  19. }  
  20. layers {  
  21.   name: "data"  
  22.   type: DATA  
  23.   top: "data"  
  24.   top: "label"  
  25.   data_param {  
  26.     source:  "val_lmdb"#lmdb格式的驗證數據  
  27.     backend: LMDB  
  28.     batch_size: 50  
  29.   }  
  30.   transform_param {  
  31.     crop_size: 227  
  32.     mirror: false  
  33.     mean_file:"imagenet_mean.binaryproto"#均值文件  
  34.   }  
  35.   include: { phase: TEST }  
  36. }  

二、h5py格式數據

上面的lmdb一般用於單標籤數據,圖片分類的時候,大部分用lmdb格式。然而假設我們要搞的項目是人臉特徵點識別,我們要識別出68個人臉特徵點,也就是相當於136維的輸出向量。網上查了一下,對於caffe多標籤輸出,需要使用h5py格式的數據,而且使用h5py的數據格式的時候,caffe是不能使用數據擴充進行相關的數據變換的,很是悲劇啊,所以如果caffe使用h5py數據格式的話,需要自己在外部,進行數據擴充,數據歸一化等相關的數據預處理操作。

1、h5py數據格式生成

下面演示一下數據h5py數據格式的製作:

[python] view plain copy
  1. # coding: utf-8  
  2. caffe_root = '/home/hjimce/caffe/'  
  3. import sys  
  4. sys.path.insert(0, caffe_root + 'python')  
  5. import os  
  6. import cv2  
  7. import numpy as np  
  8. import h5py  
  9. from common import shuffle_in_unison_scary, processImage  
  10. import matplotlib.pyplot as plt  
  11.   
  12. def readdata(filepath):  
  13.     fr=open(filepath,'r')  
  14.     filesplit=[]  
  15.     for line in fr.readlines():  
  16.         s=line.split()  
  17.         s[1:]=[float(x) for x in s[1:]]  
  18.         filesplit.append(s)  
  19.     fr.close()  
  20.     return  filesplit  
  21. #因爲我們的訓練數據可能不是正方形,然而網絡的輸入的大小是正方形圖片,爲了避免強制resize引起的圖片扭曲,所以我們採用填充的方法  
  22. def sqrtimg(img):  
  23.     height,width=img.shape[:2]  
  24.     maxlenght=max(height,width)  
  25.     sqrtimg0=np.zeros((maxlenght,maxlenght,3),dtype='uint8')  
  26.   
  27.     sqrtimg0[(maxlenght*.5-height*.5):(maxlenght*.5+height*.5),(maxlenght*.5-width*.5):(maxlenght*.5+width*.5)]=img  
  28.     return  sqrtimg0  
  29.   
  30.   
  31. def generate_hdf5():  
  32.   
  33.     labelfile =readdata('../data/my_alige_landmark.txt')  
  34.     F_imgs = []  
  35.     F_landmarks = []  
  36.   
  37.   
  38.     for i,l in enumerate(labelfile):  
  39.         imgpath='../data/'+l[0]  
  40.   
  41.         img=cv2.imread(imgpath)  
  42.         maxx=max(img.shape[0],img.shape[1])  
  43.         img=sqrtimg(img)#把輸入圖片填充成正方形,因爲我們要訓練的圖片的大小是正方形的圖片255*255  
  44.         img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#圖片轉爲灰度圖像  
  45.         f_face=cv2.resize(img,(39,39))#把圖片縮放成255*255的圖片  
  46.         # F  
  47.         plt.imshow(f_face,cmap='gray')  
  48.   
  49.   
  50.         f_face = f_face.reshape((13939))  
  51.         f_landmark =np.asarray(l[1:],dtype='float')  
  52.   
  53.         F_imgs.append(f_face)  
  54.   
  55.   
  56.         #歸一化人臉特徵點標籤,因爲上面height等於width,這裏比較懶,直接簡寫  
  57.         f_landmark=f_landmark/maxx #歸一化到0~1之間  
  58.         print f_landmark  
  59.         F_landmarks.append(f_landmark)  
  60.   
  61.   
  62.     F_imgs, F_landmarks = np.asarray(F_imgs), np.asarray(F_landmarks)  
  63.   
  64.   
  65.     F_imgs = processImage(F_imgs)#圖片預處理,包含均值歸一化,方差歸一化等  
  66.     shuffle_in_unison_scary(F_imgs, F_landmarks)#打亂數據  
  67.   
  68.     #生成h5py格式  
  69.     with h5py.File(os.getcwd()+ '/train_data.h5''w') as f:  
  70.         f['data'] = F_imgs.astype(np.float32)  
  71.         f['landmark'] = F_landmarks.astype(np.float32)  
  72.     #因爲caffe的輸入h5py不是直接使用上面的數據,而是需要調用.txt格式的文件  
  73.     with open(os.getcwd() + '/train.txt''w') as f:  
  74.         f.write(os.getcwd() + '/train_data.h5\n')  
  75.     print i  
  76.   
  77.   
  78. if __name__ == '__main__':  
  79.     generate_hdf5()  

利用上面的代碼,可以生成一個train.txt、train_data.h5的文件,然後在caffe的prototxt中,進行訓練的時候,可以用如下的代碼,作爲數據層的調用:

[python] view plain copy
  1. layer {  
  2.     name: "hdf5_train_data"  
  3.     type: "HDF5Data"  #需要更改類型  
  4.     top: "data"  
  5.     top: "landmark"  
  6.     include {  
  7.         phase: TRAIN  
  8.     }  
  9.     hdf5_data_param {   #這個參數類型h5f5_data_param記得要更改  
  10.         source: "h5py/train.txt" #上面生成的train.txt文件  
  11.         batch_size: 64  
  12.     }  
  13. }  

上面需要注意的是,相比與lmdb的數據格式,我們需要該動的地方,我標註的地方就是需要改動的地方,還有h5py不支持數據變換。

2、h5py數據讀取

[python] view plain copy
  1. f=h5py.File('../h5py/train.h5','r')  
  2. x=f['data'][:]  
  3. x=np.asarray(x,dtype='float32')  
  4. y=f['label'][:]  
  5. y=np.asarray(y,dtype='float32')  
  6. print x.shape  
  7. print y.shape  

可以通過上面代碼,查看我們生成的.h5格式文件。

在需要注意的是,我們輸入caffe的h5py圖片數據爲四維矩陣(number_samples,nchannels,height,width)的矩陣,標籤矩陣爲二維(number_samples,labels_ndim),同時數據的格式需要轉成float32,用於迴歸任務。

**********************作者:hjimce   時間:2015.10.2  聯繫QQ:1393852684  原創文章,轉載請保留原文地址、作者等信息***************
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章