2016年2月16日 星期二

Matlab同時使用多核CPU和多張GPU加速

CPU的Multi-thread用法類似

p = gcp();
for i = 1 : iter
    f(i) = parfeval(p, @myFunc, ...);
end
for i = 1 : iter
    fetchNext(f);
end

而GPU則只需將陣列使用gpuArray宣告即可。但因為要使用多GPU,因此先使用

nGPU = gpuDeviceCount;

得到GPU的個數,然後呼叫數學函數前記得先用gpuDevice()選擇要用哪張GPU

gpuDevice(GPU_index) % 1 <= GPU_index <= nGPU
math_func(...)
...

搭配前面的CPU multi-thread即可同時使用多張GPU運算。


注意事項:
    使用Multi-thread CPU + Multi-GPU時,記得[1]要自己安排讓#(nGPU)個函式用CPU+GPU執行,[2]剩下#(nCPU-nGPU)個只使用CPU單獨。[1]可避免太容易出現GPU out of memory,[2]可增加效能。

2016年2月14日 星期日

mac上使用multi-thread(OpenMP)

OpenMP是常見的multi-thread library,他有不同的實作。而Mac使用的compiler有gcc或clang,因此OpenMP也有相對應的實作。

參考:
gcc: https://gcc.gnu.org/projects/gomp/
clang: http://clang-omp.github.io/

2016年1月28日 星期四

AMD顯示卡玩Diablo 3閃退/畫面卡住問題

用AMD的顯示卡玩暗黑3第二章淒涼砂地時,容易發生閃退或是畫面停住不動的情形,這是因為某次暗黑改版後開始發生和新的AMD驅動程式(15.x)不相容的情形。官方的解法是將顯示卡驅動程式版本降到13.12,只是不是每個作業系統都可以這麼容易的降版本(e.g Windows 10),且為了玩暗黑而降低驅動版本聽起來也是怪怪的。因此,一個比較好的解法是,仍然降低驅動版本,但不是改變整個系統的驅動,而是只改變暗黑自己使用的驅動版本。方法是將AMD驅動的動態函式庫直接放到遊戲安裝目錄下,這樣遊戲就會優先讀取安裝目錄下的驅動了。

可以從以下網址
https://mega.nz/#F!8tlAhDyQ!2AkAQukJKAYuIWEzDTGtFA!IslFkTSL
選擇13.12.zip來下載,將目錄下的.dll檔都丟進遊戲安裝目錄就可以了。

2016年1月26日 星期二

小技巧

使用numpy的array時,若我們想取出其中一個值,且使用類似
ary[1,2]
的方式,結果會是一個ndim為0的array。若我們希望可以維持結果的ndim還是和原本一樣,但又不想用
ary[1,2,None,None]
這種自己添加singleton的方式的話,還可以使用
ary[1:2,2:3]
另外,就算array的某個維度的大小是1,使用ary[..., :, ...]的方式取值(而不是
ary[..., 0, ...])的話,就能保持該維度1的大小。

在python中,若要判斷一個值是否為零,應該使用
if not val
而不是像C的
if ~val
因為~是binary的complement

使用numpy的array時,若要複製一個array到另一個變數,而不只是指向相同instant的話,應該使用
A = np.copy(B)
而不是
A = B

使用terminal連到其他電腦時,若要運行一個要跑很久的程式,可能會想在執行後將terminal關掉,將來再連回去,這時可以使用screen指令。首先在執行程式之前,執行
screen
然後接下來正常執行要跑的程式即可。當想要離開時,按下
Ctrl+a
然後在按
d
即可detach這個screen。想要回復screen,只要使用
screen -r
即可。更多使用可參考
http://blogger.gtwang.org/2013/11/screen-command-examples-to-manage-linux-terminals.html
Ctrl+a [ 可進入游標移動模式

ipython實用快捷鍵
http://cmdlinetips.com/2013/01/eleven-ipython-keyboard-shortcuts-to-move-around-easily/

python中print參數的方法
http://www.python-course.eu/python3_formatted_output.php

我們可以使用x=gpuArray(...)來建立一個儲存在GPU中的矩陣,若要確定這個變數是存在GPU中,可以使用class(x),會發現他確實是一個gpuArray的物件。此外,可以使用whos指令來查看目前工作區的變數資訊,可以發現gpuArray的物件大小都固定是108個Bytes,這是因為此變數確實是占用gpu的記憶體。



2016年1月24日 星期日

使用caffe.Net創建和讀取model

我們可以使用
caffe.Net('net.prototxt', caffe.TRAIN)
來創建新的網路,也可以使用
caffe.Net('net.prototxt', 'pretrain.caffemodel', caffe.TRAIN)
來讀取已經train過的model。其中有兩點事項:

(1)最後一個參數caffe.TRAIN或是caffe.TEST指的是要讀取net.prototxt中的phase TRAIN還是phase TEST的網路,兩個網路的差別就在於那些include不同phase的layer。而若使用solver來讀取網路,他會幫我們兩個都讀取,並分別放在solver.net和solver.test_nets裡面。
(2)當我們讀進預先訓練好的網路時,若我們使用
net.blobs['layer_name'].data
查看blobs資料時,會發現全部都是0。這並不是表示我們的model檔沒有儲存成功,因為其實網路裡面儲存資料的部分除了有layer和layer間的資料,layer本身也有許多參數要儲存,而我們訓練一個網路實際上是在修正layer本身的參數(forward改變layer和layer間的資料,backward改變layer本身的參數),因此需要儲存的資料也就是layer的參數,而不是layer和layer間的資料。若想要確認,應該要使用[1]
net.params['layer_name'][0].data
但這些參數對我們來說其實也沒什麼特別的意義,因此通常我們在讀進網路資料後,就是先forward某筆測試資料,然後再將某層blob的資料取出。

2016年1月23日 星期六

一些筆記2

可以直接使用matlab的mat檔來當作caffe網路輸入的資料庫資料,但有注意事項:
(1) caffe和python中numpy的array是相同的格式(row major),和matlab(column major)不同。因此,在用matlab輸出mat檔前,記得要用permute()將資料dimension的order倒過來。且若要使用reshape(),維度順序也要注意不要弄錯。(應該是先permute後再做reshape)
(2)在caffe的prototxt中,資料層記得要使用HDF5Data,參數使用hdf5_data_param,source一個.txt檔,裡面放真正hdf5檔(也就是matlab的mat檔)的位置。
(3)python的h5py可以直接讀取matlab的mat檔,因為mat檔本身是hdf5格式。但如果是從python創建hdf5檔首先要注意(i)我們要將檔案關閉後他才會寫進硬碟裡,再來是(ii)matlab必須要使用
python:
f = h5py.File('filename')
f.create_dataset('datasetname', data=data_ary)
f.close()

matlab:
data = hdf5read('filename','datasetname')
才能讀取[3][5]。


python界面使用caffe的方法:
(1)首先我們當然要先定義我們網路的內容,除了直接手寫prototxt檔之外,也可以使用python界面來產生。根據caffenet.py的範例,首先是將layers和params這兩個modules import進來,接下來就可以用
[top layer] = [layer type]([bottom layer], [parameters ...])
的形式一層層連接layer,最後完成我們的網路。接下來import net_spec.py中的to_proto功能,將剛才定義的網路的頂層輸入這個函數,他就會回傳一個proto資料結構,我們可以使用print功能來查看剛才定義的網路長相,也可以將print的結果導出到文字檔中,就完成我們的prototxt檔了。

(2)有了網路的定義,接下來就是產生真正的網路資料結構。import pycaffe的Net(或直接import caffe),使用
net = caffe.Net('proto_name.prototxt', caffe.TEST)
就可以產生一個網路資料的變數了。也可以在中間加入train過的model檔路徑來得到train過的model變數
net = caffe.Net('proto_name.prototxt', 'model.caffemodel', caffe.TEST)

net中的blobs包含了每層的資料資訊,使用net.blobs.items()可以列出所有blobs名稱。使用blob = net.blobs['blob_name']得到blob後,可用blob.data得到blob中的資料。
此外,也可以使用
solver = caffe.SGDSolver('solver_name.prototxt')
solver中除了也包含了solver.net的資訊外,也可以利用solver.solve()來自動forward、backward網路。若要使用之前的snapshot來回復solver的資訊,可以使用
solver.restore('snapshot.solverstate')
若是想要直接使用train好的model來finetune,可使用
net.copy_from('modelname. caffemodel')
或
solver.net.copy_from('modelname. caffemodel')

net的forward()和backward()所需要的參數中,blobs是一個list的字串,start和end都是一個字串,這些字串都是layer的名稱(使用net.blobs.keys()可以查看)。因為呼叫forward()和backward()後,會回傳某些layer中的資料回來,blobs參數就是讓我們自定義還想"多"回傳哪些layer的blob。start和end用來表示要forward和backward的layer區間。kwargs是一個dict,可以讓我們指定某個layer要使用什麼樣的資料,我們可以使用
{'data1':val1, 'data2':val2, ...}
來創建dict。其中'data1'、'data2'是資料的名稱,也就是data layer中的那些top名稱。而val1、val2則是資料本體。我們也可以使用另一種寫法:
net.forward_all(data1=val1, data2=val2, ...)


caffe的layer中有一個叫做HDF5Output,用處是將我們網路的結果輸出。若要使用,要注意他的input要有兩個[4](通常是一個data、一個label),然後使用
hdf5_output_param {
    file_name: "result.h5"
}
指定輸出檔案名稱。看起來很方便,其實沒什麼用,因為他一筆訓練資料只能輸出一個數字,可能是分類結果、精確度或誤差,但若想要把一個大blob輸出是不行的。


pycaffe中,forward、backward的參數kwargs其實是python中的keyword arguments,可以自己指定input,並會以dict的方式傳入函數[6]。


在caffe中使用dummy_layer的方式
layer {
  name: "dummy"
  type: "DummyData"
  top: "dummy"
  dummy_data_param {
    shape {
      dim: ...
      dim: ...
    }
    data_filler {
      type: "uniform"
      min: ...
      max: ...
    }
  }
}
小心網路上的範例把layer打成layers,會出錯誤。