前面已經安裝好了torch,下面就來看看如何在torch框架上搭建深度學習模型,我一直覺得源碼結合原理是機器學習最好的學習途徑。所以我們從分析一個簡單的案例開始吧。
這個例子呢,主要是以有監督的方式構建一個深度學習模型實現對數據集SVHN的分類。
SVHN是 The Street View House Numbers Dataset, 數據集介紹見 SVHN數據集
代碼主要分為五個部分
-
數據的預處理
-
網絡模型的構建
-
損失函數的定義
-
訓練網絡
-
測試數據
數據的預處理
- require 'torch' -- torch
- require 'image' -- to visualize the dataset
- require 'nn' -- provides a normalization operator
加載頭文件
- if not opt then
- print '==> processing options'
- cmd = torch.CmdLine()
- cmd:text()
- cmd:text('SVHN Dataset Preprocessing')
- cmd:text()
- cmd:text('Options:')
- cmd:option('-size', 'small', 'how many samples do we load: small | full | extra')
- cmd:option('-visualize', true, 'visualize input data and weights during training')
- cmd:text()
- opt = cmd:parse(arg or {})
- end
文件的命令行參數。主要有兩個參數(文件大小和是否可視化選項),torch.CmdLine()函數參見torch.CmdLine()
- www = 'http://data.neuflow.org/data/housenumbers/'
-
- train_file = 'train_32x32.t7'
- test_file = 'test_32x32.t7'
- extra_file = 'extra_32x32.t7'
-
- if not paths.filep(train_file) then
- os.execute('wget ' .. www .. train_file)
- end
- if not paths.filep(test_file) then
- os.execute('wget ' .. www .. test_file)
- end
- if opt.size == 'extra' and not paths.filep(extra_file) then
- os.execute('wget ' .. www .. extra_file)
- end
用於數據集的下載,數據集網址,但是這個網址好像被牆了,訪問不了。所以我自己令下載的數據集SVHN,其中只下載了 train_32x32.mat和 test_32x32.mat文件,因為數據太大機子跑得太慢。
順便說一句上邊代碼中 os.execute(string)是執行string指令,wget是下載指令,參見linux 應用之wget 命令詳解
下載下來的數據是 mat格式的,要轉換成 torch使用的t7格式,文檔中說可以使用mattorch工具實現,但是我在虛擬機上沒有裝matlab,所以安裝mattorch總是失敗。 另外使用matio同樣可以實現matlab和torch間數據轉換。
下面是安裝matio的指令matio-ffi
- sudo apt-get install libmatio2
- sudo luarocks install matio
此時下載的數據是 columns x rows x channels x num ,但image.display()要求的數據組織形式是: num x channels x columns x rows,所以需要進行重組織,由於我也是個剛開始使用torch沒一周的人,所以就用較原始的辦法重組織了,誰有好辦法希望教教我!下面是數據轉換的代碼
- matio = require'matio'
- loaded = matio.load('/SVHN_Data/train_32x32.mat')
- tempData=loaded.X:permute(4,3,1,2)
- trainData = {
- data = tempData,
- labels = loaded.y[{{},{1}}], -- loaded.y:size() --> 26032 x 1
- size = function() return trsize end
- }
數據存放在'/SVHN_Data'文件夾內
------------------------------------------------------下面一段是用來看數據轉換的對不對 --------------------------------
torch 結果

matlab結果

顏色不大一樣,一個是在筆記本上跑的,一個是在台機上跑的,不知道是不是機器的原因還是什么原因
---------------------------------------------------------------------END---------------------------------------------------------
- if opt.size == 'extra' then
- print '==> using extra training data'
- trsize = 73257 + 531131
- tesize = 26032
- elseif opt.size == 'full' then
- print '==> using regular, full training data'
- trsize = 73257
- tesize = 26032
- elseif opt.size == 'small' then
- print '==> using reduced training data, for fast experiments'
- trsize = 10000
- tesize = 2000
- end
上面這一段是設置訓練集和測試集的大小。
================================================================= START ==================================================
- loaded = torch.load(train_file,'ascii')
- trainData = {
- data = loaded.X:transpose(3,4),
- labels = loaded.y[1],
- size = function() return trsize end
- }
上面這段代碼很容易理解,就是分別將數據和標簽起別名data和labels方便后續使用,size返回的是訓練樣本的個數。唯一需要注意的是transpose()函數的使用,這是因為在matlab中數據的表達一般是先列后行,而在torch中數據的表達一般是先行后列,所以這里對后兩維進行了轉置
這段代碼被上面自己下載數據並處理取代
====================================================== END ======================================================
- if opt.size == 'extra' then
- loaded = torch.load(extra_file,'ascii')
- trdata = torch.Tensor(trsize,3,32,32)
- trdata[{ {1,(#trainData.data)[1]} }] = trainData.data
- trdata[{ {(#trainData.data)[1]+1,-1} }] = loaded.X:transpose(3,4)
- trlabels = torch.Tensor(trsize)
- trlabels[{ {1,(#trainData.labels)[1]} }] = trainData.labels
- trlabels[{ {(#trainData.labels)[1]+1,-1} }] = loaded.y[1]
- trainData = {
- data = trdata,
- labels = trlabels,
- size = function() return trsize end
- }
- end
當數據選擇extra時,上面對訓練集進行拼接。
同樣加載測試集
- loaded = matio.load('/SVHN_Data/test_32x32.mat')
- tempData = loaded.X:permute(4,3,1,2)
- testData = {data = tempData, labels =loaded.y, size = function() return tesize end}
- tempData = nil
下面進行數據的預處理
數據的預處理包含三個trick
+ 圖像從RGB空間映射到YUV空間
+ Y通道使用 contrastive normalization operator進行局部規范化
+ 對所有的數據在每個通道進行規范化到0,1之間
- -- RGB==>YUV
- for i=1,trainData:size() do
- trainData.data[i] = image.rgb2yuv(trainData.data[i]) -- 等價於 trainData.data[{{i},{},{},{}}]
- end
- for i=1,testData:size() do
- testData.data[i] = image.rgb2yuv(testData.data[i])
- end
-
- -- Name Channels for convenience
- channels = {'y','u','v'}
-
-
- -- 單通道進行規范化
- Mean={}
- Std={}
- for i=1, channel in ipairs(channels) do --此處和for i=1,3 do等價
- Mean[i]= trainData.data[{{},{i},{},{}}]:mean()
- Std[i] = trainData.data[{{},{i},{},{}}]:std()
- trainData.data[{{},{i},{},{}}]=trainData.data[{{},{i},{},{}}]:csub(Mean[i])
- trainData.data[{{},{i},{},{}}]=trainData.data[{{},{i},{},{}}]:div(Std[i])
- end
-
- for i=1,3 do
- testData.data[{{},{i},{},{}}]:add(-Mean[i]) -- add 和csub
- -- 這個用法見Tensor的手冊,改變后替代原來數據,所以和上面是一樣的
- testData.data[{{},{i},{},{}}]:div(Std[i])
- end
- -- 至於為什么測試數據使用訓練集的統計量歸一化,參見機器學習相關理論
Y通道局部的規范化需要使用nn包里的算子
- -- Define the normalization neighborhood:
- neighborhood = image.gaussian1D(7)
-
- -- Define our local normalization operator (It is an actual nn module,
- -- which could be inserted into a trainable model):
- normalization = nn.SpatialContrastiveNormalization(1, neighborhood):float()
-
- -- Normalize all Y channels locally:
- for i = 1,trainData:size() do
- trainData.data[{ i,{1},{},{} }] = normalization:forward(trainData.data[{ i,{1},{},{} }]) --前向計算
- end
- for i = 1,testData:size() do
- testData.data[{ i,{1},{},{} }] = normalization:forward(testData.data[{ i,{1},{},{} }])
- end
關於函數 nn.SpatialContrastiveNormalization(1, neighborhood) 參見 torch/nn/SpatialContrastiveNormalization.lua
===================== It's always good practice to verify that data is properly normalized ========================
- for i,channel in ipairs(channels) do
- trainMean = trainData.data[{ {},i }]:mean()
- trainStd = trainData.data[{ {},i }]:std()
-
- testMean = testData.data[{ {},i }]:mean()
- testStd = testData.data[{ {},i }]:std()
-
- print('training data, '..channel..'-channel, mean: ' .. trainMean)
- print('training data, '..channel..'-channel, standard deviation: ' .. trainStd)
-
- print('test data, '..channel..'-channel, mean: ' .. testMean)
- print('test data, '..channel..'-channel, standard deviation: ' .. testStd)
- end
================================================= END ======================================
最后是數據的可視化,顯示了前256個數據Y,U,V通道上的效果
- if opt.visualize then
- first256Samples_y = trainData.data[{ {1,256},1 }]
- first256Samples_u = trainData.data[{ {1,256},2 }]
- first256Samples_v = trainData.data[{ {1,256},3 }]
- image.display{image=first256Samples_y, nrow=16, legend='Some training examples: Y channel'}
- image.display{image=first256Samples_u, nrow=16, legend='Some training examples: U channel'}
- image.display{image=first256Samples_v, nrow=16, legend='Some training examples: V channel'}
- end
具體的代碼見附件
命令行執行: (1_data.lua)是文件名
- qlua 1_data.lua
結果見下圖(Y通道)
github上給的結果(Y通道)
================================================== 結論 ===================================
-
torch 挺好用的,和我胃口-
-
在筆記本上安裝虛擬機跑深度學習的代碼。。。真是蠻拼的。。。這速度感人啊,直接在ubuntu系統上跑還是蠻快的
===========================================================================================
=
