使用GPU在caffe上進行CNN訓練

1.配置caffe環境

[請參考此篇博客:http://blog.csdn.net/ws_20100/article/details/48850449]

本篇介紹如何在caffe環境下,實現"圖像對圖像"的卷積神經網絡的訓練。



2.文件結構

在配置好的caffe文件夾中,進入examples目錄,創建CNN文件夾,並進入文件夾

$ cd caffe-master/examples/

$ mkdir CNN

$ cd CNN/

CNN文件夾下面創建子文件夾

$ mkdir model snapshot TestPhotos TestLabels TrainPhotos TrainLabels

其中,

model用於以後存儲卷積核矩陣和偏置向量;

snapshot用於存儲訓練中備份的caffe模型,每一段時間存儲一次,防止斷電等一些情況;

TrainPhotosTrainLabels分別存儲訓練集輸入和監督樣本;

TestPhotosTestLabels分別存儲測試集輸入和監督樣本,不直接參與到訓練中。

然後,將訓練所用的輸入樣本和監督樣本分別放入到TrainPhotos和TrainLabels中去。注意,樣本文件名無所謂,但是排列次序必須一一對應。同樣,將測試所用的輸入樣本和監督樣本分別放入到TestPhotos和TestLabels中去。


3.產生訓練和測試數據

1.)產生路徑文件

CNN文件夾下面(以下均是在此文件夾下)創建兩個路徑文件。

$ vim train.txt

輸入內容:

examples/CNN/train.h5

:wq保存文檔。

$ vim test.txt

輸入內容:

examples/CNN/test.h5

:wq保存文檔。

2.)產生訓練數據

$ vim generate_train.m

輸入內容:(把輸入圖像切成11*11的像素塊,監督圖像爲3*3的像素塊(由網絡結構和卷積核大小決定),步長爲1個像素)

clear;close all;

%% settings
folder_input = 'TrainPhotos';
folder_label = 'TrainLabels';
savepath = 'train.h5';
size_input = 11;
size_label = 3; % size_input - 12
stride = 1;

%% initialization
data = zeros(size_input, size_input, 1, 1);
label = zeros(size_label, size_label, 1, 1);
padding = abs(size_input - size_label) / 2;
count = 0;

%% read data
filepaths_input = dir(fullfile(folder_input,'*.jpg'));
filepaths_label = dir(fullfile(folder_label,'*.jpg'));

if (length(filepaths_input)==length(filepaths_label))
    length = length(filepaths_input);
else
    error('The Number of Input is NOT equal to the Number of Label.');
end

%% generate data
for i = 1 : length
    
    im_input = imread(fullfile(folder_input,filepaths_input(i).name));
    im_input = rgb2ycbcr(im_input);
    im_input = im2double(im_input(:, :, 1));
    
    im_label = imread(fullfile(folder_label,filepaths_label(i).name));
    im_label = im2double(im_label(:, :, 1));

    if size(im_input) == size(im_label)
        [hei,wid] = size(im_input);
    else
        error('The size of input and label are not equal.');
    end
    
    for x = 1 : stride : hei-size_input+1
        for y = 1 :stride : wid-size_input+1
            
            subim_input = im_input(x : x+size_input-1, y : y+size_input-1);
            subim_label = im_label(x+padding : x+padding+size_label-1, y+padding : y+padding+size_label-1);

            count = count + 1;
            data(:, :, 1, count) = subim_input;
            label(:, :, 1, count) = subim_label;
        end
    end
    
end

%% randomized the data and label 
order = randperm(count);
data = data(:, :, 1, order);
label = label(:, :, 1, order); 

%% writing to HDF5
chunksz = 128;
created_flag = false;
totalct = 0;

for batchno = 1:floor(count/chunksz)
    last_read=(batchno-1)*chunksz;
    batchdata = data(:,:,1,last_read+1:last_read+chunksz); 
    batchlabs = label(:,:,1,last_read+1:last_read+chunksz);

    startloc = struct('dat',[1,1,1,totalct+1], 'lab', [1,1,1,totalct+1]);
    curr_dat_sz = store2hdf5(savepath, batchdata, batchlabs, ~created_flag, startloc, chunksz); 
    created_flag = true;
    totalct = curr_dat_sz(end);
end
h5disp(savepath);

終端下輸入:

$ matlab -nodesktop -nosplash -logfile generate_train.log -r generate_train

產生訓練數據train.h5

3.)產生測試數據

$ vim generate_test.m

generate_test.m只需要將generate_test.m文件開頭改爲:

clear;close all;

%% settings
folder_input = 'TestPhotos';
folder_label = 'TestLabels';
savepath = 'test.h5';
size_input = 11;
size_label = 3;
stride = 30;

將最後一段改成:

%% writing to HDF5
chunksz = 2;
created_flag = false;
totalct = 0;

for batchno = 1:floor(count/chunksz)
    last_read=(batchno-1)*chunksz;
    batchdata = data(:,:,1,last_read+1:last_read+chunksz); 
    batchlabs = label(:,:,1,last_read+1:last_read+chunksz);

    startloc = struct('dat',[1,1,1,totalct+1], 'lab', [1,1,1,totalct+1]);
    curr_dat_sz = store2hdf5(savepath, batchdata, batchlabs, ~created_flag, startloc, chunksz); 
    created_flag = true;
    totalct = curr_dat_sz(end);
end
h5disp(savepath);

終端下輸入:

$ matlab -nodesktop -nosplash -logfile generate_test.log -r generate_test

產生測試數據test.h5。僅僅用於判斷訓練到達什麼地步。


4.建立訓練文件

1.)建立solver文件

$ vim CNN_solver.prototxt

此爲運行的配置文件,輸入以下內容:

# The train/test net protocol buffer definition
net: "examples/CNN/CNN_net.prototxt"
test_iter: 556
# Carry out testing every 500 training iterations.
test_interval: 500
# The base learning rate, momentum and the weight decay of the network.
base_lr: 0.0001
momentum: 0.9
weight_decay: 0
# The learning rate policy
lr_policy: "fixed"
# Display every 100 iterations
display: 100
# The maximum number of iterations
max_iter: 15000000
# snapshot intermediate results
snapshot: 500
snapshot_prefix: "examples/CNN/snapshot/CNN"
# solver mode: CPU or GPU
solver_mode: GPU

:wq保存退出。

2.)建立net文件

$ vim CNN_net.prototxt

此爲網絡結構配置文件,可以配置網絡層數,節點數,卷積核等參數。輸入以下內容:

name: "CNN"
layer {
  name: "data"
  type: "HDF5Data"
  top: "data"
  top: "label"
  hdf5_data_param {
    source: "examples/CNN/train.txt"
    batch_size: 128
  }
  include: { phase: TRAIN }
}
layer {
  name: "data"
  type: "HDF5Data"
  top: "data"
  top: "label"
  hdf5_data_param {
    source: "examples/CNN/test.txt"
    batch_size: 2
  }
  include: { phase: TEST }
}

layer {
  name: "conv1"
  type: "Convolution"
  bottom: "data"
  top: "conv1"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 0.1
  }
  convolution_param {
    num_output: 128
    kernel_size: 5
    stride: 1
    pad: 0
    weight_filler {
      type: "gaussian"
      std: 0.001
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}

layer {
  name: "relu1"
  type: "ReLU"
  bottom: "conv1"
  top: "conv1"
}

layer {
  name: "conv2"
  type: "Convolution"
  bottom: "conv1"
  top: "conv2"
  param {
    lr_mult: 1
  }
  param {
    lr_mult: 0.1
  }
  convolution_param {
    num_output: 64
    kernel_size: 3
    stride: 1
    pad: 0
    weight_filler {
      type: "gaussian"
      std: 0.001
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}

layer {
  name: "relu2"
  type: "ReLU"
  bottom: "conv2"
  top: "conv2"
}

layer {
  name: "conv3"
  type: "Convolution"
  bottom: "conv2"
  top: "conv3"
  param {
    lr_mult: 0.1
  }
  param {
    lr_mult: 0.1
  }
  convolution_param {
    num_output: 1
    kernel_size: 3
    stride: 1
    pad: 0
    weight_filler {
      type: "gaussian"
      std: 0.001
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}

layer {
  name: "loss"
  type: "EuclideanLoss"
  bottom: "conv3"
  bottom: "label"
  top: "loss"
}

:wq保存退出。


5.CNN訓練

$ vim train.sh

輸入以下shell:

#!/bin/bash
cd ../../
./build/tools/caffe train --solver examples/CNN/CNN_solver.prototxt 2>&1 | tee examples/CNN/CNN.log

增加運行權限:

$ chmod +x train.sh

運行腳本文件:

$ ./train.sh

時間可能會運行幾天,也可以提前退出(Ctrl+C),因爲在snapshot中有中間備份存儲。



6.保存濾波器

1.)創建mat文件

$ cp CNN_net.prototxt CNN_mat.prototxt

CNN_mat.prototxt文件開頭兩個layer段改爲:

name: "CNN"
input: "data"
input_dim: 1
input_dim: 1
input_dim: 11
input_dim: 11

input: "label"
input_dim: 1
input_dim: 1
input_dim: 3
input_dim: 3

:wq保存即可。

2.)創建M文件

$ vim saveFilters.m

輸入以下內容:(第7行可以更改需要轉換的caffemodel文件名)

caffe.reset_all();
clear; close all;
%% settings
%folder = 'examples/CNN/';
folder = './';
model = [folder 'CNN_mat.prototxt'];
weights = [folder 'snapshot/CNN_iter_550000.caffemodel'];
savepath = [folder 'model/x.mat'];
layers = 3;

%% load model using mat_caffe
net = caffe.Net(model,weights,'test');

%% reshap parameters
weights_conv = cell(layers,1);

for idx = 1 : layers
    conv_filters = net.layers(['conv' num2str(idx)]).params(1).get_data();
    [~,fsize,channel,fnum] = size(conv_filters);

    if channel == 1
        weights = double(ones(fsize^2, fnum));
    else
        weights = double(ones(channel, fsize^2, fnum));
    end
    
    for i = 1 : channel
        for j = 1 : fnum
             temp = conv_filters(:,:,i,j);
             if channel == 1
                weights(:,j) = temp(:);
             else
                weights(i,:,j) = temp(:);
             end
        end
    end

    weights_conv{idx} = weights;
end

%% save parameters
weights_conv1 = weights_conv{1};
weights_conv2 = weights_conv{2};
weights_conv3 = weights_conv{3};
biases_conv1 = double(net.layers('conv1').params(2).get_data());
biases_conv2 = double(net.layers('conv2').params(2).get_data());
biases_conv3 = double(net.layers('conv3').params(2).get_data());

save(savepath,'weights_conv1','biases_conv1','weights_conv2','biases_conv2','weights_conv3','biases_conv3');

:wq保存。

3.)運行M文件

$ matlab -nodesktop -nosplash -logfile saveFilters.log -r saveFilters

此時,在model中會生成x.mat文件。


7.CNN重構

已經知道了x.mat文件中,有三層卷積層的卷積核矩陣weights_conv*和偏置向量biases_conv*。

編寫一個demo_net.m文件,使用這些參數構建卷積網絡結構,對輸入圖像(矩陣)進行處理,即可得到結果。

不同應用有不同的源碼,這裏省略該文件源碼。


本文可能敘述不全面,如有錯誤,歡迎指正!

Enjoy~~

發佈了43 篇原創文章 · 獲贊 42 · 訪問量 39萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章