【caffe-Windows】以mnist为例的hdf5单标签处理——matlab实现
前言
主要是想嘗試看一下多標簽的caffe是如何進行輸入輸出的,但是目前還未找到基于原始caffe做多標簽輸入的代碼,大部分都是基于源碼做了一部分修改實現多標簽分類,caffe官網倒是有一個多標簽的Python程序,這個我慢慢研究研究,此篇博客先看看單標簽的數據格式制作與訓練,以hdf5和mnist數據集為例吧
【注】使用hdf5的好處有三個:①相對于前面制作的lmdb和leveldb數據集,用convert的那個代碼轉換前,一般都要求一張圖片一個標簽,也就是說貓圖片的標簽必須是“貓”,而不是”綠“、“貓”,而hdf5的數據格式就能支持后者,一張圖片可有多個標簽描述。②當我們的數據是非圖片類型的,比如一維聲音數據,也能用hdf5制作滿足caffe輸入的數據集。③當程序無法一次性讀取太大的數據集,我們可以將數據集分別放到不同的hdf5文件
國際慣例,參考博客:
caffe HDF5Data 層使用及數據生成
解讀創建hdf5的matlab程序
在caffe中自帶了hdf5的處理程序,我們先來研究研究,然后再套入mnist數據集。
- 前面說過一個數據集可以放入不同的hdf5文件中,那么我們肯定要現指定當前的數據存在哪個hdf5文件中
- 由于自帶的程序中沒有調用指定數據集,所以事先隨機生成了一系列樣本
? 需要注意的是這個數據集的格式,data_disk的每個維度分別代表寬、高、通道數、樣本數,剛好符合opencv的讀取方式,而matlab讀取圖片的方式是高、寬,與這個剛好相反,所以用自己的數據集記得要翻轉前兩個維度,待會再說。此外label_disk的兩個維度分別是標簽的單熱度編碼、樣本數,與我們的習慣也相反,我們習慣一行一個標簽,而這里是一列是一個樣本的標簽。
然后我們看看到底是怎么寫入文件的?
定義了三個變量,分別是每次讀取的數據大小,是否覆蓋寫入原來的hdf5文件(1/0)
chunksz=100; created_flag=false; totalct=0;看不懂沒關系,接著往下讀代碼
fprintf('batch no. %d\n', batchno);last_read=(batchno-1)*chunksz;% to simulate maximum data to be held in memory before dumping to hdf5 file batchdata=data_disk(:,:,1,last_read+1:last_read+chunksz); batchlabs=label_disk(:,last_read+1:last_read+chunksz);這里就是按照事先設置的chunksz分批讀取數據。比如第一次讀取第1-100個數據,第二次就是101-200,以此類推,當然讀取數據的時候,雖然從每批的第一個數據(1/101/201…)開始讀,但是有時候我們需要從這個數據的第m個樣本的第n行第m列開始讀,所以提供了這樣一個結構體,用于指示讀取數據的開始位置
startloc=struct('dat',[1,1,1,totalct+1], 'lab', [1,totalct+1]);也就是從totalct+1個數據的第1行第1列第1個通道開始讀取,標簽同理
接下來就是存儲每批數據到最開始指定的trial.h5文件中了
curr_dat_sz=store2hdf5(filename, batchdata, batchlabs, ~created_flag, startloc, chunksz);有興趣可以看看store2hdf5的源碼,這里分別介紹一下幾個參數的含義:
- filename:存儲的hdf5文件名稱
- batchdata:讀取的數據,一般是四個維度(寬、高、通道、批大小)
- batchlabs:讀取的批數據對應的輸出,可以是標簽也可以是其它的東東,兩維(類別、批大小)
- created_flag:是否覆蓋寫入(0不覆蓋,1覆蓋),一般來說第一個批次肯定是覆蓋寫入(~false=1)
- startloc:前面說的第一個數據開始位置
- chunksz:批大小
- curr_dat_sz:返回值,四維(寬、高、通道、已寫入樣本數)
接下來兩個就是重置3中的下一個讀取數據的位置totalct和4中的是否覆蓋寫入為否(0=~true)
created_flag=true;% flag set so that file is created only oncetotalct=curr_dat_sz(end);% updated dataset size (#samples)【注】代碼靈活多變,請不要循環類似于“為什么函數store2hdf5用~created_flag而不把非號去掉?”諸如此類的問題。
后面有一行代碼用于展示存儲的hdf5數據情況
h5disp(filename);自帶的程序輸出如下,一般我們核對核對大小和樣本數就行咯:
HDF5 trial.h5 Group '/' Dataset 'data' Size: 5x5x1x10000MaxSize: 5x5x1xInfDatatype: H5T_IEEE_F32LE (single)ChunkSize: 5x5x1x100Filters: noneFillValue: 0.000000Dataset 'label' Size: 10x10000MaxSize: 10xInfDatatype: H5T_IEEE_F32LE (single)ChunkSize: 10x100Filters: noneFillValue: 0.000000這樣還不夠,我們還得檢測一下數據是不是真的是我們輸入的數據,也就是一致性
data_rd=h5read(filename, '/data', [1 1 1 1000], [5, 5, 1, 1000]); label_rd=h5read(filename, '/label', [1 1000], [10, 1000]); fprintf('Testing ...\n'); try assert(isequal(data_rd, single(data_disk(:,:,:,1000:1999))), 'Data do not match');assert(isequal(label_rd, single(label_disk(:,1000:1999))), 'Labels do not match');fprintf('Success!\n'); catch errfprintf('Test failed ...\n');getReport(err) end? 可以發現讀取了存儲的trial.h5文件中從第[1 1 1 1000]個數據開始的隨后1000個每個大小[5,5,1]的數據。其實也就是按照[5,5,1]從第1000個樣本的第一個數值讀,總共讀1000個就行了,即完成了讀取第1000-1999的樣本數據和標簽。隨后在try catch語句中判斷這讀出來的和我們原始數據data_disk和label_disk是不是對應的,是就返回Success!,錯誤的話不僅返回Test failed ...而且還有錯誤原因,非常便于調試。
最后,當然就是指示一下我們的hdf5文件名字是什么,為什么用txt文檔存?因為前面已經說了,一個大的數據集一般存在多個hdf5中,換行存儲就行。自帶源碼只存儲到了一個hdf5文件中,因而對應的list.txt存儲只有一行,那就是:
trial.h5在prototxt中使用hdf5的方法,其實也就是換掉了type類型和指定hdf5存儲的txt位置
layer {name: "data"type: "HDF5Data"top: "data"top: "labelvec"hdf5_data_param {source: "/path/to/list.txt"batch_size: 64} }以mnist為實例創建hdf5文件
制作訓練集
去顯示mnist手寫數字這一博文中copy一下loadMNISTImages和loadMNISTLabels這兩個文件,用于讀取存儲mnist的四個二進制文件,哪四個就不說了吧t10k-images-idx3-ubyte、t10k-labels-idx1-ubyte、train-images-idx3-ubyte、train-labels-idx1-ubyte
然后按照上面介紹的基本流程分別進行如下修改:
數據集的讀取
data = loadMNISTImages('t10k-images-idx3-ubyte')'; labels = loadMNISTLabels('t10k-labels-idx1-ubyte'); num_total_samples=size(data,1); data_disk=reshape(data,[28,28,1,num_total_samples]);%將數據集讀取出來(高*寬*通道*樣本數)圖片寬高翻轉和標簽的行列變換
data_disk=permute(data_disk,[2 1 3 4]);%翻轉matlab讀取的高寬 label_disk=permute(labels,[2 1]);%轉換為caffe標簽的輸入格式(類別數*總樣本數)驗證一致性
data_rd=h5read(filename, '/data', [1 1 1 1000], [28, 28, 1, 1000]); label_rd=h5read(filename, '/label', [1 1000], [1, 1000]);附上train.h5制作的完整代碼,注意test.h5的制作相同,只需替換代碼中的train為test以及測試數據對應的二進制文件即可。
%% WRITING TO HDF5 clear clc filename='train.h5';%可改test.h5% data = loadMNISTImages('train-images-idx3-ubyte')';%可改test的數據集 % labels = loadMNISTLabels('train-labels-idx1-ubyte');%可改test的標簽 data = loadMNISTImages('train-images-idx3-ubyte')'; labels = loadMNISTLabels('train-labels-idx1-ubyte'); num_total_samples=size(data,1); data_disk=reshape(data,[28,28,1,num_total_samples]);%將數據集讀取出來(高*寬*通道*樣本數) %由于caffe正常的處理方法是opencv讀取圖片,與matlab的高寬相反 data_disk=permute(data_disk,[2 1 3 4]);%翻轉matlab讀取的高寬 label_disk=permute(labels,[2 1]);%轉換為caffe標簽的輸入格式(類別數*總樣本數) % num_total_samples=10000; % to simulate data being read from disk / generated etc. % data_disk=rand(5,5,1,num_total_samples); % label_disk=rand(10,num_total_samples); chunksz=100; created_flag=false; totalct=0; for batchno=1:num_total_samples/chunksz fprintf('batch no. %d\n', batchno); last_read=(batchno-1)*chunksz;% to simulate maximum data to be held in memory before dumping to hdf5 file batchdata=data_disk(:,:,1,last_read+1:last_read+chunksz); batchlabs=label_disk(:,last_read+1:last_read+chunksz);% store to hdf5 startloc=struct('dat',[1,1,1,totalct+1], 'lab', [1,totalct+1]); curr_dat_sz=store2hdf5(filename, batchdata, batchlabs, ~created_flag, startloc, chunksz); %1代表新建,0代表附加 created_flag=true;% flag set so that file is created only once totalct=curr_dat_sz(end);% updated dataset size (#samples) end% display structure of the stored HDF5 file h5disp(filename);%% READING FROM HDF5% Read data and labels for samples #1000 to 1999 data_rd=h5read(filename, '/data', [1 1 1 1000], [28, 28, 1, 1000]); label_rd=h5read(filename, '/label', [1 1000], [1, 1000]); fprintf('Testing ...\n'); try assert(isequal(data_rd, single(data_disk(:,:,:,1000:1999))), 'Data do not match'); assert(isequal(label_rd, single(label_disk(:,1000:1999))), 'Labels do not match');fprintf('Success!\n'); catch err fprintf('Test failed ...\n'); getReport(err) end%delete(filename);% CREATE list.txt containing filename, to be used as source for HDF5_DATA_LAYER FILE=fopen('train.txt', 'w');%可改test.txt fprintf(FILE, '%s', filename); fclose(FILE); fprintf('HDF5 filename listed in %s \n', 'list.txt');% NOTE: In net definition prototxt, use list.txt as input to HDF5_DATA as: % layer { % name: "data" % type: "HDF5Data" % top: "data" % top: "labelvec" % hdf5_data_param { % source: "/path/to/list.txt" % batch_size: 64 % } % }運行結果,總共600個batch:
HDF5 test.h5 Group '/' Dataset 'data' Size: 28x28x1x60000MaxSize: 28x28x1xInfDatatype: H5T_IEEE_F32LE (single)ChunkSize: 28x28x1x100Filters: noneFillValue: 0.000000Dataset 'label' Size: 1x60000MaxSize: 1xInfDatatype: H5T_IEEE_F32LE (single)ChunkSize: 1x100Filters: noneFillValue: 0.000000 Testing ... Success! HDF5 filename listed in list.txt同理制作test.h5文件。
以hdf5為輸入進行訓練
把原始的lenet_train_test.prototxt的前兩個’layer’改一下:
name: "LeNet" layer { name: "mnist" type: "HDF5Data" top: "data" top: "label" include {phase: TRAIN } hdf5_data_param {source: "train.txt"batch_size: 64 } } layer { name: "mnist" type: "HDF5Data" top: "data" top: "label" include {phase: TEST } hdf5_data_param {source: "test.txt"batch_size: 100 } }寫個bat文件訓練試試
E:\caffeDEV1\caffe-master\Build\x64\Release\caffe.exe train --solver=lenet_solver.prototxt pause截取了部分運行過程
I0621 11:01:55.571880 10520 solver.cpp:244] Train net output #0: loss = 2.29 63 (* 1 = 2.2963 loss) I0621 11:01:55.571880 10520 sgd_solver.cpp:106] Iteration 6600, lr = 0.00683784 I0621 11:01:56.463932 10520 solver.cpp:228] Iteration 6700, loss = 2.29493 I0621 11:01:56.464931 10520 solver.cpp:244] Train net output #0: loss = 2.29 493 (* 1 = 2.29493 loss) I0621 11:01:56.464931 10520 sgd_solver.cpp:106] Iteration 6700, lr = 0.00680711 I0621 11:01:57.424986 10520 solver.cpp:228] Iteration 6800, loss = 2.28502 I0621 11:01:57.425987 10520 solver.cpp:244] Train net output #0: loss = 2.28 502 (* 1 = 2.28502 loss) I0621 11:01:57.425987 10520 sgd_solver.cpp:106] Iteration 6800, lr = 0.0067767 I0621 11:01:58.465046 10520 solver.cpp:228] Iteration 6900, loss = 2.29951 I0621 11:01:58.466047 10520 solver.cpp:244] Train net output #0: loss = 2.29 951 (* 1 = 2.29951 loss) I0621 11:01:58.466047 10520 sgd_solver.cpp:106] Iteration 6900, lr = 0.0067466 I0621 11:01:59.259091 10520 solver.cpp:337] Iteration 7000, Testing net (#0) I0621 11:01:59.883127 10520 solver.cpp:404] Test net output #0: accuracy = 0 .1049 I0621 11:01:59.884127 10520 solver.cpp:404] Test net output #1: loss = 2.385 52 (* 1 = 2.38552 loss) I0621 11:01:59.889127 10520 solver.cpp:228] Iteration 7000, loss = 2.29426 I0621 11:01:59.889127 10520 solver.cpp:244] Train net output #0: loss = 2.29 426 (* 1 = 2.29426 loss) I0621 11:01:59.889127 10520 sgd_solver.cpp:106] Iteration 7000, lr = 0.00671681好了,附件打包:
程序和prototxt以及bat下載地址: 鏈接:http://pan.baidu.com/s/1hr4UM2o 密碼:bqiy
mnist手寫數字:鏈接:http://pan.baidu.com/s/1jHYCQJ8 密碼:vaeo
后續研究將擴展到多標簽中,預先參考文獻戳這里
?
總結
以上是生活随笔為你收集整理的【caffe-Windows】以mnist为例的hdf5单标签处理——matlab实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 损失函数梯度对比-均方差和交叉熵
- 下一篇: 【caffe-Windows】基于Pyt