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,會出錯誤。

2016年1月11日 星期一

一些筆記

http://nbviewer.ipython.org/github/BVLC/caffe/blob/master/examples/00-classification.ipynb
官方推薦的pycaffe教學

http://wubinblog.com/deep%20learning/2015/07/13/Use_Caffe_For_Classification/
http://blog.csdn.net/deeplearninglc007/article/details/40086503
http://closure11.com/caffe%E5%9C%A8python%E4%B8%AD%E4%BD%BF%E7%94%A8%E5%86%85%E5%AD%98%E6%95%B0%E6%8D%AEmemorydata%E8%BF%9B%E8%A1%8C%E8%AE%AD%E7%BB%83/
Caffe中文文章

http://deepdish.io/2015/04/28/creating-lmdb-in-python/
用python創建LMDB的教學,但label只能一個數字

https://groups.google.com/forum/#!topic/caffe-users/6OOcM-XfvOI
提到如何將testing時最後的label輸出

http://blog.csdn.net/visionfans/article/details/48400147
reshape時發現blob的維度超過上限了

https://github.com/Russell91/nlpcaffe/issues/2
https://github.com/BVLC/caffe/issues/2006
遇到儲存snapshot時發生錯誤,可能是因為資料太大,要將維度降低

http://dirlt.com/caffe.html
http://www.cnblogs.com/dupuleng/articles/4370296.html
中文教學,有提到train_val.prototxt和deploy.prototxt的差別,基本上train_val需要指定訓練資料路徑,表示是用來訓練(改變)model的,而deploy則只有指定輸入資料的大小形狀,是model訓練完後拿來用的(不改變model)。

https://groups.google.com/forum/#!topic/caffe-users/8J_J8tc1ZHc
什麼是lr_mult和decay_mult,以及為何有兩個(一個是weight,一個是bias)

https://www.quora.com/Is-there-a-recurrent-neural-networks-toolkit
提到有哪些RNN的工具

http://jeffdonahue.com/lrcn/
caffe有RNN的版本
https://github.com/BVLC/caffe/pull/2033
介紹

https://developer.apple.com/library/mac/documentation/Accelerate/Reference/BLAS_Ref/#//apple_ref/c/func/cblas_sgemm
一些blas的api

http://deeplearning.net/tutorial/lstm.html
LSTM介紹

http://openhome.cc/Gossip/Python/WithAs.html
python中with as的教學。基本上就是簡化讀寫檔案時所需的try...except...final。

http://tech.seety.org/python/python_imaging.html
python PIL影像處理函式庫

http://stackoverflow.com/questions/874461/read-mat-files-in-python
python讀matlab的mat檔的方式

http://stackoverflow.com/questions/120656/directory-listing-in-python
在python中使用os列出目錄資訊的方法。其中,os.walk回傳的generator是種有iterator功能的東西
https://wiki.python.org/moin/Generators
可利用他enumerate的功能來做快速迭代
http://stackoverflow.com/questions/522563/accessing-the-index-in-python-for-loops
http://www.cnblogs.com/vivilisa/archive/2009/03/19/1417083.html

http://www.tutorialspoint.com/python/python_tuples.htm
在python中,使用[1,2,3]是list,使用(1,2,3)是tuple,兩者的差異在於tuple是不能改變其內容的(immutable),其他用法

http://stackoverflow.com/questions/4344017/how-can-i-get-the-concatenation-of-two-lists-in-python-without-modifying-either
可利用list1+list2將兩個list接起來

http://stackoverflow.com/questions/4151128/what-are-the-differences-between-numpy-arrays-and-matrices-which-one-should-i-u
numpy中,array和matrix的差別在於:matrix只能是二維的,而array可以是任意d維的。

http://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.matrix.html
宣告一個matrix的方法

http://docs.scipy.org/doc/numpy-1.10.0/reference/generated/numpy.concatenate.html
將兩個array接起來的方法

找出array中非零個數的方法
http://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.nonzero.html

PIL Image儲存影像的方法
http://stackoverflow.com/questions/14452824/how-can-i-save-an-image-with-pil

http://effbot.org/imagingbook/decoder.htm
http://stackoverflow.com/questions/16720682/pil-cannot-write-mode-f-to-jpeg
影像的mode種類,有RGB、grayscale等,及其設定方法

array增加singleton的方法(singleton就是大小只有1的維度,它應該可以刪除,但有時候array形狀有限定時會需要增加這個維度)
http://stackoverflow.com/questions/9510252/efficient-way-to-add-a-singleton-dimension-to-a-numpy-vector-so-that-slice-assig

http://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.reshape.html
reshape的用法。array中resize和reshape的差別:reshape是回傳resize後的array,resize是直接改變array本身

http://stackoverflow.com/questions/53162/how-can-i-do-a-line-break-line-continuation-in-python
python要將一行code打成多行,只要直接換行就好了,但是縮排還是要縮

http://mathesaurus.sourceforge.net/matlab-numpy.html
python和matlab函式的對應表

2016年1月4日 星期一

easy_install & pip

非常不錯的教學網站

http://www.openfoundry.org/tw/tech-column/8536-introduction-of-python-extension-management-tools

筆記:在Mac上安裝Caffe(後續)

接著我打算開始使用python界面的pycaffe。從基本的開始,先將caffe根目錄下的python資料夾加入環境變數PYTHONPATH
export PYTHONPATH="/path/to/caffe-master/python"
記得"不是"
export PYTHONPATH="/path/to/caffe-master/build/python"
也"不是"
export PYTHONPATH="/path/to/caffe-master/python/caffe"

接著進入python的shell(or prompt),並執行
import caffe
過程中出現錯誤訊息
Segmentation fault: 11
並且直接跳出python程式。Mac系統比較容易出現這個問題,根據網路上的資料[1],原因是Mac系統有內建python,但我們是用自己下載的版本(brew python or anaconda python),而在build pycaffe的時候,很容易混入系統內建python,造成連結錯誤。
    打開build資料夾下的CMakeCache.txt,檢查裡面所有有關python的資訊,果然發現兩行
PYTHON_INCLUDE_DIR:PATH=/System/Library/Frameworks/Python.framework/Headers
PYTHON_LIBRARY:FILEPATH=/usr/lib/libpython2.7.dylib
表示python的include資料夾以及函式庫都聯結到系統的了。將其改為
PYTHON_INCLUDE_DIR:PATH=/Users/lindol/anaconda/include/python2.7
PYTHON_LIBRARY:FILEPATH=/Users/lindol/anaconda/lib/libpython2.7.dylib
並且重新編譯
make clean
make all -j4
結束後再次執行
import caffe
錯誤資訊改變:
ImportError: No module named google.protobuf.internal
表示我python的搜尋路徑中並沒有包含這個module。由於protobuf是由brew安裝的,若今天使用的是同樣brew安裝的python的話,這個module是會被找到的,但問題我們使用的是anaconda python,因此這個library的搜尋路徑並沒有被設定好。有關於設定python的搜尋路徑有許多方法,根據網路上的資料,我選擇使用增加usercustomize.py的方式[2][3]。在目錄下新增usercustomize.py:
/path/to/caffe-master/python/usercustomize.py
且內容是讓python找到protobuf的方法
import site; site.addsitedir('/usr/local/Cellar/protobuf/2.6.1/libexec/lib/python2.7/site-packages')
再次執行
import caffe
終於沒有錯誤。

在import caffe時,若出現
No module named skimage.io
可使用
pip install scikit-image
安裝相關模組[4]。

參考:



2016年1月3日 星期日

python import

import是python用來將其他modules匯入目前modules的功能。

我們想要import進來的東西應該可以分為兩種
ㄧ)獨立的module,其路徑應該長得像
/path/m1.py
二)包在package裡面的module,其路徑應該長得像
/path/pkg_path/m2.py
在第二種case中,pkg_path之所以可以顯得和一般的path不同,是因為我們在資料夾下手動加入了一個檔案
/path/pkg_path/__init__.py
有了這個檔案,python就會知道這個資料夾下是放了屬於同一個package的module。這個檔案是可以沒有內容的。
    接下來,不管是哪種case,假如我們想要每次打開python都可以直接import,都應該要將他的路徑加到環境變數中[1]:
export PYTHONPATH="/path:$PYTHONPATH"
注意這裡第二種case"不應該"使用
export PYTHONPATH="/path/pkg_path:$PYTHONPATH"
因為pkg_path整個資料夾算一個單位,若使用後面這種方式import,會使得python可以獨立找到其中的module,也就是下面這種用法會被允許:
m2.func()
,但較正確的用法應該是
pkg_path.m2.func()
也就是說,可以使這些module有pkg_path這個命名空間。
    當我們設定好環境變數,我們可以進入python,看他是不是真的會將PYTHONPATH底下的路徑加入搜尋路徑:
import sys
sys.path

接下來就是import我們的module了,使用
import m1
就可以使用m1裡的功能了
m1.func()
直接執行
m1
就能發現他確實將m1變數連接到m1.pyc這個編譯好的檔案。
    由於我們在pkg_path中有加入__init__.py,因此我們可以執行
import pkg_path
若執行
pkg_path
可以發現變數連接到的就是__init__.pyc。但目前只有將pkg_path這個命名空間加入,還沒有真正將module匯入。執行
import pkg_path.m2
會幫我們加入pkg_path這個命名空間,以及pkg_path.m2這個module。此時才能夠使用
pkg_path.m2.func()

接下來是from ... import ... [3]
我們可以使用
from namespace import module (等同import namespace.module)
from namespace.module import mem
from module import mem
但不能使用
from namespace import module.mem
簡單來說就是要from目前python可以找到的路徑開始,然後import後面的東西,不論是namespace、module(module其實也是一種namespace)或mem。但是mem都要等到他所屬的module存在sys.modules中,才能被使用,只有module以上的namespace被加入是不能使用mem的。

from m1 import func1
from pkg_path.m2 import func1, func2, func3
上面程式具體行為其實類似
func1 = m1.func1
func1 = pkg_path.m2.func1
...
(python中也可以使用變數指向一個function)
也就是說,其實我們是在自己這個module裡面新增一個變數指向import後面的東西,from後面的東西並沒有特別用一個變數去指它。而python會將import時所經過的所有module(namespace)都加入sys.modules。我們之所以在import之後就可以直接使用import後面的東西,是因為新增了一個local變數存這個東西(module、function等),而將module匯入其實是另一件事了,因為即使目前sys.modules中確實是有這個module存在,但若這個指向他的變數不見了,我們依然無法使用這個module。
    因此回去看上面的程式其實會發現有個問題,就是func1被assign了兩次,因此我們最後其實只能使用到pkg_path.m2.func1。這也是為什麼我們不推薦使用
from ... import *
也就是import namespace下所有的東西進來。這樣做可能會造成衝突,因為很有可能有東西的名稱是重複的。
    但如果我們要import的東西名稱真的是重複的,我們可以使用
import name1 as name2
這樣import進來時所新增的變數名稱就能變成name2。最後,這個用法是可以和上面混合的:
from ... import ... as ...

新增:
from .module import ...
這個用法中,.module的那個點代表的是目前的module[8]。比如說,在一個package中可能有很多.py檔,.module就是代表同一個package中的其他module。

其他:
global():列出module namespace。(Note. 在command line(prompt)中直接import的都算global)
local():列出目current namespace。[4]

其他學習:[5][6][7]

參考:
[1] http://stackoverflow.com/questions/3144089/expand-python-search-path-to-other-source
[2] http://stackoverflow.com/questions/448271/what-is-init-py-for
[3] http://stackoverflow.com/questions/9439480/from-import-vs-import
[4] http://stackoverflow.com/questions/7969949/whats-the-difference-between-globals-locals-and-vars
[5] http://openhome.cc/Gossip/Python/ModuleABC.html
[6] http://openhome.cc/Gossip/Python/ImportModule.html
[7] http://openhome.cc/Gossip/Python/ImportImportAsFrom.html
[8] http://stackoverflow.com/questions/7279810/what-does-a-in-an-import-statement-in-python-mean

2016年1月2日 星期六

cmake腳本中印出訊息的方法

message(STATUS "${var}")
message(STATUS "abc")

筆記:在Mac上安裝Caffe(CPU only)

這篇文章其實不算安裝Caffe的教學,比較像是我自己在安裝的過程中遇到的問題以及所學知識的筆記。

根據官方教學

在Mac上需要做的事
http://caffe.berkeleyvision.org/install_osx.html

編譯Caffe需要做的事
http://caffe.berkeleyvision.org/installation.html#compilation

brew tap指令用來增加brew對資源庫的搜尋[1]。
brew tap <user/repo> <URL>
可以讓brew安裝特定URL的repo下的軟體

若要移除tap過的repo,可使用
brew untap user/repo [user/repo user/repo ...]

brew install option介紹
brew install -vd
-v: verbose
-d: debug

Homebrew是使用git來管理你所下載的repo,local的repo可以在電腦的/usr/local/裡面找到(.git/.gitignore)。以opencv為例,我們可以使用
brew edit opencv
來編輯brew對opencv安裝及編譯的腳本。我們可以利用git來查看我們對腳本所做過的修改,也可以復原我們所做的修改。首先,要使用git查看我們所安裝過的repo前,應該先執行
brew update
更新brew的local repo資訊,接下來就可以
cd /usr/local/

cd $(brew --prefix)
進入repo所在資料夾,然後用
git status
查看修改過的項目了。若要將修改復原,也是使用git,利用reset、checkout等指令將repo回復[17][18]。

snappy: 快速壓縮/解壓縮軟體[2]
leveldb: 資料庫軟體[3]
lmdb: 資料庫軟體[4]
gflags : Commandline flags module for C++[5]
glog : Logging library for C++[6]
szip: HDF裡面使用的壓縮程式[7]
hdf5: 一種儲存和管理資料的格式和函式庫[8]
opencv: 影像處理函式庫
protobuf: 一種輕便高效的結構化數據儲存格式,可將數據進行序列化和反序列化[9][10]
boost: 一群有廣泛功能的函式庫[11]
BLAS: Basic Linear Algebra Subprograms(基礎線性代數程式集),用於線性代數計算的界面規範(or API)[12]
OpenBLAS: Open source的BLAS實作[13]
MKL: Math Kernel Library,Intel的數學函式庫[14]
Clang: 由蘋果公司贊助開發的編譯器前端,以LLVM為後端,希望作為GCC的替代品[15]
Atlas: Automatically Tuned Linear Algebra Software,一個以BLAS API開發的線性代數函式庫[16]


其中OpenBLAS、MKL和Atlas都是BLAS API的實作。Mac OS中本身也有包含BLAS的實作,也就是Accelerate和vecLib函式庫。在安裝Caffe時可以根據情況設定使用上述不同的BLAS函式庫。
    leveldb、lmdb和hdf5都是用來儲存大量數據時使用的資料庫,他們在記憶體使用量及效能上有些差異[24][25],但都能在caffe中使用。
    protobuf是一種儲存格式,在caffe中就是用來制訂每個layer的種類、關係等。


homebrew/science是homebrew收納的函式庫中其中的一個來源。全部函式庫來源可參考homebrew的github首頁:
https://github.com/Homebrew


想要查看所有安裝過的python package,可在python shell/prompt下執行
help('modules')
或是直接在普通command line下執行
pydoc modules

在python中查看當前python使用的版本
import sys
print (sys.version)

pip指令是python下的套件管理工具[19]

Anaconda python是python眾多發行版本中的其中一個,此版本包含了許多好用的功能在裡面。在Mac中會安裝在使用者的Home目錄下(~/anaconda),刪掉此資料夾就可以移除。因為每個版本的python執行檔都是獨立分開的,因此互相不會衝突,但是要使用特定版本時也要指定對應的路徑。

根據官方教學,在brew編譯及安裝python版opencv的腳本中(執行brew edit opencv),有兩行類似
-DPYTHON_LIBRARY=#{py_prefix}/lib/libpython2.7.dylib 
-DPYTHON_INCLUDE_DIR=#{py_prefix}/include/python2.7
(我們可以在vim中用"/"來搜尋)
這兩行是用來規範我們將要用來編譯opencv的python的路徑。在這兩行上方可以找到py_prefix的定義
py_prefix = `python-config --prefix`.chomp
這表示真正py_prefix其實是由一個python-config檔來決定。若我們cd進入系統python的執行檔目錄下,真的可以看到這個python-config檔
cd /usr/bin | grep python
且這個檔本身也是個腳本。因為若打開這個檔,我們可以看到這個"--prefix"確實是這個腳本中的一個參數。順便一提,在Mac中的python-config是沒有"--configdir"這個參數選項的,所以brew的腳本中才會寫到"OS.linux?"這個判斷式吧。
    回到原本的問題,我們這裡要做到的是利用官方很推薦的Anaconda python來幫我們的opencv編譯,但是根據brew的腳本,系統首先會找到的是/usr/bin/下的python-config檔,因此最終會使用的python也是/usr/bin/下的python,而不是我們家目錄的Anaconda python。因此我們需要做的就是改變環境變數,使得家目錄下的Anaconda python優先被找到。
首先利用
export PATH="$HOME/anaconda/bin:$PATH"
將bin資料夾加入PATH,然後
export DYLD_FALLBACK_LIBRARY_PATH="$HOME/anaconda/lib:/usr/local/lib:/usr/lib:$DYLD_FALLBACK_LIBRARY_PATH"
將其他的lib資料夾也加一加。
    這裡有幾點事項,以Mac來說,這兩行應該寫到家目錄下~/.bash_profile中,這樣每次開新terminal才不用再加一次。再來是其實我們安裝anaconda時它其實已經會自動幫我們把export bin的指令加入~/.bash_profile中,所以也不用自己加。最後,export lib資料夾到DYLD_FALLBACK_LIBRARY其實是官方網站的建議,但根據brew編譯opencv的腳本,其實它自己會找的到anaconda的lib,所以理論上我們也不用自己加入...anyway,現在應該已經可以開始編譯anaconda版的opencv了,因為我之前已經先安裝過一次普通版的,所以先執行
brew uninstall opencv
將原先的移除,然後再安裝一次。附帶一提,這時突然出現一個很跳tone的問題,就是brew提示我的一個函式庫numpy的link有問題,要執行
brew link numpy
來修復,但修復到一半又出現什麼f2py已經存在,等等等問題。這裡的重點是,在brew出現這些奇怪提示時,非常建議使用
brew doctor
這個指令,他會幫我們列出許多潛在可能出現的問題,幫助我們解決這些疑難雜症。

依賴函式庫都裝完後,就可以開始正式編譯Caffe了。有兩種方式:
一)先修改目錄底下的Makefile.config.example文件符合自己的電腦系統配備,並將其檔名改為Makefile.config,再執行make
二)直接執行cmake,他會偵測我們的電腦配備,並自動產生合適的Makefile[23]。接下來我們再執行make就好了
這裡我選擇第二種方式。執行下列指令:
mkdir build 
cd build 
cmake .. 
make all -j4
出現下列問題
問題1:
error: expected function body after function declarator
extern void ATLU_DestroyThreadMemory() __OSX_AVAILABLE_BUT_DEPRECATED(__MAC_10_2, __MAC_10_9, __IPHONE_4_0, __IPHONE_NA);
原因:
是不知道為何,一個在10.9 sdk中的header檔A,include另一個header檔B,而這個B竟然是include到10.8 sdk中的檔。其實在10.9中也有和B同名的header檔,但不知道為何系統會優先去找10.8的。

解法:暫時先利用將xcode目錄下
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk
這個資料夾名稱改掉(前面加個底線就好),但是不確定這樣是否系統會接著找10.9 sdk,但至少這樣改錯誤資訊不一樣了...

問題2[20]:
Building with 'Xcode Clang++'.
ld: library not found for -lpython2
clang: error: linker command failed with exit code 1 (use -v to see invocation)
我以為這個問題是出在我的python library路徑沒有設對,所以去查看了cmake檔中的設定
build/CMakeCache.txt
真的發現不知為何PYTHON_INCLUDE_DIR和PYTHON_LIBRARY這兩個的位置都指到系統的python位置,而不是我所用的anaconda python,所以手動將兩個改掉:
PYTHON_INCLUDE_DIR:PATH=/Users/lindol/anaconda/include/python2.7
PYTHON_LIBRARY:FILEPATH=/Users/lindol/anaconda/lib/libpython2.7.dylib
再次make,發現錯誤資訊回到問題2,表示問題2可能和python路徑沒有關...(*更新)。後來發現其實這個步驟是在build matlab的東西時發生的錯誤,目前也不知道該如何處理,但因為已經花太多時間在build環境,暫時先關閉build matcaffe,讓make能順利跑完。

後續:在runtest時跑出HDF5 library版本不符的問題,但因為我裝的(1.8.16)其實比他要求的(1.8.15)更新,所以我覺得應該沒有問題。在環境變數中設定
export HDF5_DISABLE_VERSION_CHECK="1"
來跳過錯誤訊息。

其他安裝教學[21][22]

*(2016/1/21 更新)後來安裝別人發佈的RNN版caffe,發現將這兩個路徑更改正確非常重要,不然會很容易出現
Segmentation fault 11
的錯誤

參考:
[1] https://github.com/Homebrew/homebrew/blob/master/share/doc/homebrew/brew-tap.md
[2] http://brewformulas.org/Snappy
[3] https://zh.wikipedia.org/wiki/LevelDB
[4] http://symas.com/mdb/
[5] http://brewformulas.org/Gflags
[6] http://brewformulas.org/Glog
[7] https://www.hdfgroup.org/doc_resource/SZIP/
[8] https://www.hdfgroup.org/HDF5/
[9] https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/
[10] http://www.searchtb.com/2012/09/protocol-buffers.html
[11] http://blog.monkeypotion.net/gameprog/note/first-touch-of-boost-cpp-libraries
[12] https://zh.wikipedia.org/wiki/BLAS
[13] http://www.openblas.net/
[14] https://software.intel.com/en-us/intel-mkl
[15] https://zh.wikipedia.org/wiki/Clang
[16] https://en.wikipedia.org/wiki/Automatically_Tuned_Linear_Algebra_Software
[17] http://stackoverflow.com/questions/9369519/reset-homebrew-formula
[18] http://stackoverflow.com/questions/3987683/homebrew-install-specific-version-of-formula
[19] https://blog.longwin.com.tw/2014/08/python-setup-pip-package-2014/
[20] https://github.com/BVLC/caffe/issues/915
[21] http://hoondy.com/2015/04/03/how-to-install-caffe-on-mac-os-x-10-10-for-dummies-like-me/
[22] http://coldmooon.github.io/2015/08/03/caffe_install/
[23] https://github.com/BVLC/caffe/pull/1667
[24] http://wubinblog.com/deep%20learning/2015/07/13/Use_Caffe_For_Classification/
[25] https://www.readability.com/articles/u2ejbfkc


2016年1月1日 星期五

PATH or LD_LIBRARY_PATH

這兩個變數都可以藉由env指令來修改搜尋路徑。

PATH:主要是放bin資料夾路徑(e.g. /usr/local/bin:/usr/bin:/bin ...),也就是放binary可執行檔,以方便從shell執行指令時可以找的到指令檔。

LD_LIBRARY_PATH:類似這種名稱的路徑(e.g. DYLD_FALLBACK_LIBRARY_PATH)是用來放lib資料夾路徑的(e.g. /usr/local/lib:/usr/lib:/lib ...),裡面會包含的是lib函式庫檔。而LD表示這些資料夾放的函式庫會屬於動態連結函式庫。

因此若要使用export指令新增path時,應該要根據path是屬於bin還是lib來選擇要加入哪個變數。

其他還有像PYTHONPATH(e.g. /usr/local/lib/python:/usr/lib/python ...)是python的函式庫路徑。

參考:
[1] http://unix.stackexchange.com/questions/44990/what-is-the-difference-between-path-and-ld-library-path

.bash_profile or .bashrc

在一般的linux/unix系統中,
.bash_profile:會在登入時被呼叫,包含一般的登入畫面,以及遠端登入。
.bashrc:會在打開新的ternimal視窗時呼叫,或是直接執行bash以新增另一個bash instance時呼叫。

Mac OS是個例外,在打開新的ternimal視窗時會呼叫.bash_profile

若你想要讓兩個檔案做同樣的事,但又不想每次都修改兩個檔案,可以在.bash_profile中加入:
if [ -f ~/.bashrc ]; then 
    source ~/.bashrc 
fi
這樣每次登入時,.bash_profile就會執行和.bashrc一樣的事囉!

參考: