pytorch:EDSR 生成訓練數據的方法

Pytorch:EDSR 生成訓練數據的方法

引言

Winter is cominggit

正文

pytorch提供的DataLoader 是用來包裝你的數據的工具. 因此你要將本身的 (numpy array 或其餘)github

數據形式裝換成 Tensor, 而後再放進這個包裝器中. 使用 DataLoader 有什麼好處呢?算法

就是他們幫你有效地迭代數據, 舉例:數據庫

import torch
import torch.utils.data as Data #utils是torch中的一個模塊,Data是進行小批訓練的途徑或模塊

x = torch.linspace(1, 10, 10) # x data (torch tensor): 初始的數據數組

y = torch.linspace(10, 1, 10) # y data (torch tensor): 目標的數據網絡

  • 先轉換成 torch 能識別的 Dataset
torch_dataset = Data.TensorDataset(data_tensor=x, target_tensor=y)

torch_dataset 爲用 torch 定義的一個數據庫,而後將要訓練的數據放到數據庫中。 x爲用來訓練的數據,y爲用於算偏差的數據多線程

  • dataset 放入 DataLoader
BATCH_SIZE = 5 # 批訓練大小爲五,即每次抽取五個數據進行訓練
loader = Data.DataLoader(
    dataset=torch_dataset,      # torch TensorDataset format
    batch_size=BATCH_SIZE,      # mini batch size
    shuffle=True,               # 要不要打亂數據 (打亂比較好)
    num_workers=2,              # 多線程來讀數據,更有效率
)

咱們使用 DataLoader() 來使咱們的訓練過程變成一批一批,shuffleBool 型變量,爲真時隨機打亂數據後進行抽樣app

for epoch in range(3):   # 訓練整套數據 3 次
    for step, (batch_x, batch_y) in enumerate(loader):      
        # 每一步loader釋放一小批數據用來學習,由於一組總共有10個data,batch_size又爲5,因此訓練一次數據有2個step
        # 假設這裏就是你訓練的地方...
        # 打出來一些數據
        print('Epoch: ', epoch, '| Step: ', step, '| batch x: ', batch_x.numpy(), '| batch y: ', batch_y.numpy())

結果以下:框架

Epoch:  0 | Step:  0 | batch x:  [ 6.  7.  2.  3.  1.] | batch y:  [  5.   4.   9.   8.  10.]
Epoch:  0 | Step:  1 | batch x:  [  9.  10.   4.   8.   5.] | batch y:  [ 2.  1.  7.  3.  6.]
Epoch:  1 | Step:  0 | batch x:  [  3.   4.   2.   9.  10.] | batch y:  [ 8.  7.  9.  2.  1.]
Epoch:  1 | Step:  1 | batch x:  [ 1.  7.  8.  5.  6.] | batch y:  [ 10.   4.   3.   6.   5.]
Epoch:  2 | Step:  0 | batch x:  [ 3.  9.  2.  6.  7.] | batch y:  [ 8.  2.  9.  5.  4.]
Epoch:  2 | Step:  1 | batch x:  [ 10.   4.   8.   1.   5.] | batch y:  [  1.   7.   3.  10.   6.]

能夠看出, 每步都導出了5個數據進行學習. 而後每一個 epoch 的導出數據都是先打亂了之後再導出. 真正方便的還不是這點. 若是咱們改變一下令 BATCH_SIZE = 8, 這樣咱們就知道, step=0 會導出8個數據, 可是, step=1 時數據庫中的數據不夠 8個, 這時怎麼辦呢:dom

BATCH_SIZE = 8      # 批訓練的數據個數
for ...:
    for ...:
        ...
        print('Epoch: ', epoch, '| Step: ', step, '| batch x: ',
              batch_x.numpy(), '| batch y: ', batch_y.numpy())

結果以下:

Epoch:  0 | Step:  0 | batch x:  [  6.   7.   2.   3.   1.   9.  10.   4.] | batch y:  [  5.   4.   9.   8.  10.   2.   1.   7.]
Epoch:  0 | Step:  1 | batch x:  [ 8.  5.] | batch y:  [ 3.  6.]
Epoch:  1 | Step:  0 | batch x:  [  3.   4.   2.   9.  10.   1.   7.   8.] | batch y:  [  8.   7.   9.   2.   1.  10.   4.   3.]
Epoch:  1 | Step:  1 | batch x:  [ 5.  6.] | batch y:  [ 6.  5.]
Epoch:  2 | Step:  0 | batch x:  [  3.   9.   2.   6.   7.  10.   4.   8.] | batch y:  [ 8.  2.  9.  5.  4.  1.  7.  3.]
Epoch:  2 | Step:  1 | batch x:  [ 1.  5.] | batch y:  [ 10.   6.]

這時, 在 step=1 就只給你返回這個 epoch 中剩下的data.

在閱讀edsr的源碼時發現了下面這段代碼:

opt.seed = random.randint(1,10000)
print("Random Seed: ",opt.seed)
torch.manual_seed(opt.seed) # 爲當前cpu設置隨機種子,值爲範圍在1到10000裏的一個隨機數
if cuda:
    torch.cuda.manual_seed(opt.seed) # 爲當前gpu設置隨機種子
cudnn.banchmark = Ture

在訓練開始時,參數的初始化爲隨機的,爲了讓每次的結果都一致,咱們要設置隨機種子。

cudnn.banchmark這個方法可讓CuDNNauto-tuner自動尋找最適合當前配置的高效算法,若是每次迭代輸入不變,能夠增長,若是輸入會產生變化,則會下降計算的效率。

接下來是edsr中使用 DataLoader的方法。

print("===> Loading datasets")
    train_set = DatasetFromHdf5("path_to_dataset.h5")
    training_data_loader = DataLoader(dataset=train_set, num_workers=opt.threads, batch_size=opt.batchSize, shuffle=True)

我仔細研究了兩天發現,運行到這步時,圖片彷佛已經處理好了,由於這只是對圖片進行打亂後再輸入網絡進行訓練。

從新閱讀 master 中的 ReadMe 後發現了這兩行文字:

Prepare Training dataset

  • Please refer Code for Data Generation for creating training files.
  • Data augmentations including flipping, rotation, downsizing are adopted.

經過連接找到了具體的生成測試數據的方法,下面是edsr生成訓練數據的方式:

clear;
close all;
folder = 'path/to/train/folder';

savepath = 'edsr_x4.h5'; % 將模型保存爲edsr_x4.h5文件,x4爲該模型的放大倍數

%% scale factors
scale = 4; % 放大倍數

size_label = 192; % 最終通過調整後的圖片的大小
size_input = size_label/scale; % 輸入大小 = 最終大小/放大倍數 = 48
stride = 96; % 步長大小爲96

%% downsizing
downsizes = [1,0.7,0.5]; % 調整大小的三維向量

data = zeros(size_input, size_input, 3, 1);  % init一個名爲data的大小爲48x48x3的零矩陣
label = zeros(size_label, size_label, 3, 1); % init一個名爲label的大小爲192x192x3的零矩陣

count = 0;
margain = 0; % 應爲邊緣信息一類的變量值

%% generate data  準備數據
filepaths = []; % 聲明一個讀取文件的目錄
filepaths = [filepaths; dir(fullfile(folder, '*.png'))]; % 獲得目錄中全部圖片的列表

length(filepaths) % 圖片的個數

for i = 1 : length(filepaths) % 遍歷全部圖片
    for flip = 1: 3 % 每張圖片翻轉三次
        for degree = 1 : 4 % 從4個角度?
            for downsize = 1 : length(downsizes)
                image = imread(fullfile(folder,filepaths(i).name)); % 讀取第i張圖片
                if flip == 1 % 當flip爲1時,對圖片進行上下翻轉
                    image = flipd(image ,1);
                end
                if flip == 2 % 當flip爲2時,對圖片進行左右翻轉
                    image = flipd(image ,2);
                end
                
                image = imrotate(image, 90 * (degree - 1)); % 逆時針方向旋轉圖片0-90-180-270度(角度爲正則逆時針旋轉,爲負則順時針)
                image = imresize(image,downsizes(downsize),'bicubic'); % 經過雙三次插值的方法將圖像調整爲以前的1-0.7-0.5的大小

                if size(image,3)==3 % 當圖片爲三通道RGB圖像時,進行如下的操做
                    %image = rgb2ycbcr(image);
                    image = im2double(image);
                    im_label = modcrop(image, scale); % 這個函數將取模後的圖片賦給im_label

做者定義了一個對圖像進行處理的函數(在Matlab Doc中是找不到滴):modcrop.m,在同個文件夾下能夠找到

function imgs = modcrop(imgs, modulo)
if size(imgs,3)==1 % 灰度圖,或者能夠理解爲僅有一個y通道的圖像
    sz = size(imgs);
    sz = sz - mod(sz, modulo);
    imgs = imgs(1:sz(1), 1:sz(2));
else
    tmpsz = size(imgs); % 獲取圖片尺寸
    sz = tmpsz(1:2); % 把圖片的height和width賦給sz
    sz = sz - mod(sz, modulo); % height和width對modulo取模,並減去這個值,使得sz的大小正好能夠整除modulo
    imgs = imgs(1:sz(1), 1:sz(2),:); % 獲得新的尺寸的圖片
end

如今繼續對取模後的三通道圖片進行操做:

[hei,wid, c] = size(im_label); % 獲得這張圖片的如今的大小
                    % 使用大小爲size_label*size_label的卷積核在圖片上進行卷積,步長爲stride
                    % subim_input 做爲輸入的圖片,存入到data數組中
                    % subim_label 爲放大4倍後的圖片,存入到label數組中
                    filepaths(i).name 
                    for x = 1 + margain : stride : hei-size_label+1 - margain
                        for y = 1 + margain :stride : wid-size_label+1 - margain
                            subim_label = im_label(x : x+size_label-1, y : y+size_label-1, :);
                            subim_input = imresize(subim_label,1/scale,'bicubic');
                            % figure;
                            % imshow(subim_input);
                            % figure;
                            % imshow(subim_label);
                            count=count+1;
                            data(:, :, :, count) = subim_input; % 第count組數據
                            label(:, :, :, count) = subim_label;
                        end
                    end
                end
            end
        end
    end
end

order = randperm(count); % 生成一行從1到count的整數,打亂後返回
data = data(:, :, :, order); % 將打亂後的樣本順序返回給 data 和 label 數組
label = label(:, :, :, order);

%% writing to HDF5
chunksz = 64; % 每次寫入的數據個數
created_flag = false;
totalct = 0;

for batchno = 1:floor(count/chunksz)
    batchno
    last_read=(batchno-1)*chunksz;
    batchdata = data(:,:,:,last_read+1:last_read+chunksz); 
    batchlabs = label(:,:,:,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);

如今我已經大體明白了這個.m文件要作什麼了:

  • 初始化各類參數
  • 使用 flipping, rotation, downsizing 方法對圖片調整大小
  • 對圖片使用大小爲size_label*size_label的卷積核進行卷積獲得標籤圖,縮小後獲得輸入圖
  • 將數據打亂後寫入HDF5文件中

我在總結到這一步時恍然大悟,原來main_edsr.py文件頭部的引用中那段

form dataset import DatasetFromHdf5

代碼的意思是:從data文件夾中引用格式爲Hdf5的,你已經生成好的訓練文件(剛纔訓練好的edsr_x4.h5文件)!!!

好了,看了這麼就,不實現一下就說不過去了。但是我一點運行,matlab就報錯說我沒有一下函數。這問題也不是一兩次了,我以爲有多是我沒有訓練數據(原始圖片)的問題,因而我仿照我以前學習caffe框架下SRCNN的方法對代碼的一下部分進行了修改。

% 第三行
folder = 'train';
% 55到58行去掉註釋,我想看具體的圖片長什麼樣子
figure;
imshow(subim_input);
figure;
imshow(subim_label);

而後報錯:未定義函數或變量 'flipd',其實我以前在Matlab Doc中查找有關flip函數信息時,就發現沒有filpd這個函數了。將d去掉後發現會報下標必須爲整形的錯誤。但是flip的用法沒錯啊。索性我直接將dim=1時的函數替換爲flipud(image)即上下翻轉圖片,將dim=2時的函數替換爲fliplr(image)左右翻轉圖片。此次運行沒報錯了,可是圖片顯示非常鬼畜,而後

個人電腦就藍屏了,我*************************,啊啊啊啊啊啊

算了,我脾氣超好!

憑着剛纔的印象發現,編號爲偶數的圖片大於編號爲奇數的圖片,也就是說,咱們的subim_label的大小大於subim_input這種最基本的問題沒出錯,還好還好。重啓電腦後發現,博客還在,就是網聯不上了,又重啓一次後一切正常了。行吧,原諒你了,誰讓我上午心情好呢。

寫在後面

  • 到這一步來講,應該算是學習結束了,基本弄清楚了訓練文件是怎麼產生的昨天晚上今天早上真是收穫頗豐呢!弄清楚了不少東西,nice!我真bang(苦笑.jpg)
  • 我在舍友的呼嚕聲中把這篇學習博客完成了,剛開始還想,我打字的聲音會不會吵醒他們?,後來發現徹底是多慮了,他們呼嚕聲超大,影響我學習?!,沒有沒有,開玩笑開玩笑,把刀放下好好說話……鬼知道爲何我昨晚最晚睡(3:00 am),倒是最先起牀的(8:30 am),orz
  • Happy Birthday To nado, my dear idol

結語

真心喜歡過的人無法作朋友 由於看多幾眼 都仍是想擁有

相關文章
相關標籤/搜索