日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

多线程 python layer_在Caffe中加Python Layer的方法

發布時間:2025/3/21 python 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 多线程 python layer_在Caffe中加Python Layer的方法 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Author: Zongwei Zhou | 周縱葦

Weibo: @MrGiovanni

Email: zongweiz@asu.edu

Acknowledgement: Md Rahman Siddiquee (mrahmans@asu.edu)

Caffe的參考文檔非常少,自己改代碼需要查閱網上好多好多對的錯的討論. 這篇文章主要講怎么自己編寫python layer,從而逼格很高地在caffe中實現自己的想法. 所謂的python layer,其實就是一個自己編寫的層,用python來實現. 因為近來深度學習方向要發頂會和頂刊,光是用用Caffe,Tensorflow在數據集里頭跑個網絡已經基本不可能啦,需要具備修改底層代碼的能力.

1. 準備工作

1.1 系統配置

Ubuntu 16.04 LTS

Python 2.7.14

1.2 編譯Caffe

按照一般的caffe編譯流程(可參考官網,也可參考Install caffe in Ubuntu)就好,唯一的區別就是在Makefile.config中,把這一行修改一下:

# WITH_PYTHON_LAYER := 1

改成

WITH_PYTHON_LAYER := 1

說明我們是要使用python_layer這個功能的。然后編譯成功后,在Terminator中輸入:

$ caffe

$ python

>>> import caffe

像這樣,沒有給你報錯,說明caffe和python_layer都編譯成功啦.

1.3 添加Python路徑

寫自己的python layer勢必需要.py文件,為了讓caffe運行的時候可以找到你的py文件,接下來需要把py文件的路徑加到python的系統路徑中,步驟是:

打開Terminator

輸入vi ~/.bashrc

輸入i,進入編輯模式

在打開的文件的末尾添加

export PYTHONPATH=/path/to/my_python_layer:$PYTHONPATH

鍵入esc,:wq,回車,即可保存退出

如果這部分沒有看明白,需要上網補一下如何在Linux環境中用vim語句修改文檔的知識. 實質上就是修改一個在~/路徑下的叫.bashrc的文檔.

2. 修改代碼

首先我們定義一個要實現的目標:訓練過程中,在Softmax層和Loss層之間,加入一個Python Layer,使得這個Layer的輸入等于輸出. 換句話說,這個Layer沒有起到一點作用,正向傳播的時候y=x,反向傳播的時候導數y'=1. 因此訓練的結果應該和沒加很相似.

2.1 train_val.prototxt

這個文檔是Caffe訓練的時候,定義數據和網絡結構用的,所以如果用添加新的層,需要在這里定義. 第一步是在網絡結構的定義中找到添加Python Layer的位置,根據問題的定義,Python Layer應該在softmax和loss層之間,不過網上的prototxt大多會把這兩個層合并在一起定義,成為了

layer {

name: "loss"

type: "SoftmaxWithLoss"

bottom: "fc8_2"

bottom: "label"

top: "loss"

}

我們需要把這個層拆開,變成softmax層和loss層,根據Caffe提供的官方文檔,我們知道SoftmaxWithLoss是softmax層和MultinomialLogisticLoss的合并.

The softmax loss layer computes the multinomial logistic loss of the softmax of its inputs. [7]

那拆開后的代碼就是

layer {

name: "output_2"

type: "Softmax"

bottom: "fc8_2"

top: "output_2"

}

layer {

name: "loss"

type: "MultinomialLogisticLoss"

bottom: "output_2"

bottom: "label"

top: "loss"

}

拆完了以后就只需要把你定義的Python Layer加到它們中間就好了,注意這個層的輸出和輸出,輸入是bottom,輸出是top,這兩個值需要和上一層的softmax輸出和下一層的loss輸入對接好,就像這樣(請仔細看注釋和代碼):

layer { # softmax層

name: "output_2"

type: "Softmax"

bottom: "fc8_2" # 是上一層Fully Connected Layer的輸出

top: "output_2" # 是Softmax的輸出,Python Layer的輸入

}

layer {

type: "Python"

name: "output"

bottom: "output_2" # 要和Softmax輸出保持一致

top: "output" # Python Layer的輸出

python_param {

module: "my_layer" # 調用的Python代碼的文件名

# 也就是1.3中添加的Python路徑中有一個python文件叫my_layer.py

# Caffe通過這個文件名和Python系統路徑,找到你寫的python代碼文件

layer: "MyLayer" # my_layer.py中定義的一個類,在下文中會講到

# MyLayer是類的名字

param_str: '{ "x1": 1, "x2": 2 }' # 額外傳遞給my_layer.py的值

# 如果沒有要傳遞的值,可以不定義. 相當于給python的全局變量

# 當Python Layer比較復雜的時候會需要用到.

}

}

layer {

name: "loss"

type: "MultinomialLogisticLoss"

bottom: "output" # 要和Python Layer輸出保持一致

bottom: "label" # loss層的另一個輸入

# 因為要計算output和label間的距離

top: "loss" # loss層的輸出,即loss值

}

加完以后的參數傳遞如圖

2.2 my_layer.py

重頭戲其實就是這一部分,以上說的都是相對固定的修改,不存在什么算法層面的改動,但是python里面不一樣,可以實現很多調整和試驗性的試驗. 最最基本的就是加入一個上面定義的那個"可有可無"的Python Layer.

在Python的文件中,需要定義類,類的里面包括幾個部分:

setup( ): 用于檢查輸入的參數是否存在異常,初始化的功能.

reshape( ): 也是初始化,設定一下參數的size

forward( ): 前向傳播

backward( ): 反向傳播

結構如下:

import caffe

class MyLayer(caffe.Layer):

def setup(self, bottom, top):

pass

def reshape(self, bottom, top):

pass

def forward(self, bottom, top):

pass

def backward(self, top, propagate_down, bottom):

pass

根據需要慢慢地填充這幾個函數,關于這方面的知識,我很推薦閱讀"Guide to Convolutional Neural Networks: A Practical Application to Traffic-Sign Detection and Classification." 中的這個章節 [6].

setup()的定義:

def setup(self, bottom, top):

# 功能1: 檢查輸入輸出是否有異常

if len(bottom) != 1:

raise Exception("異常:輸入應該就一個值!")

if len(top) != 1:

raise Exception("異常:輸出應該就一個值!")

# 功能2: 初始化一些變量,后續可以使用

self.delta = np.zeros_like(bottom[0].data, dtype=np.float32)

# 功能3: 接受train_val.prototxt中設置的變量值

params = eval(self.param_str)

self.x1 = int(params["x1"])

self.x2 = int(params["x2"])

reshape()的定義:

def reshape(self, bottom, top):

# 功能1: 修改變量的size

top[0].reshape(*bottom[0].data.shape)

# 看了很多材料,我感覺這個函數就是比較雞肋的那種.

# 這個函數就像格式一樣,反正寫上就好了...

# 不知道還有其他什么功能了,歡迎補充!

forward()的定義:

這個函數可以變的花樣就多了,如果是要定義不同的loss function,可以參考[1],稍微高級一點的可以參考[2],這里就實現一個y=x的簡單功能.

def forward(self, bottom, top):

# 目標:y = x

# bottom相當于輸入x

# top相當于輸出y

top[0].data[...] = bottom[0].data[:]

# 哈哈哈哈,是不是感覺被騙了,一行代碼就完事兒了:-)

了解bottom中數據的存儲結構是比較重要的,因為參考文檔不多,我只能通過print out everything來了解bottom里面究竟存著些什么. 回想在2.1的prototxt中,我們有定義輸入Python Layer的都有什么(bottom). bottom可以有多個定義,如果像例子中的只有一個bottom: "output_2",那么bottom[0].data中就存著output_2的值,當二分類問題時也就是兩列,一列是Softmax后屬于label 0的概率,一列是Softmax后屬于label 1的概率. 當bottom定義了多個輸入的時候,即

layer {

type: "Python"

name: "output"

bottom: "output_2"

bottom: "label"

top: "output"

python_param {

...

}

}

那么按照順序,bottom[0].data中依舊存著output_2,bottom[1].data中存著label值,以此類推,可以定義到bottom[n],想用的時候調用bottom[n].data就可以了. top[n].data和bottom的情況類似,也是取決于prototxt中的定義.

想象一下,既然你可以掌控了output_2和label和其他你需要的任何值(通過bottom或者param_str定義),是不是可以在這個forward()函數里面大展身手了?

是的.

但是同時,也要負責計算這個前饋所帶來的梯度,可以自己定義變量存起來,網上修改loss函數的例子就是拿self.diff來存梯度的,不過在這個例子中,因為梯度是1,所以我沒有管它.

backward()的定義:

def backward(self, top, propagate_down, bottom):

# 由于是反向傳播,top和bottom的意義就和前向傳播反一反

# top:從loss層傳回來的值

# bottom:反向層的輸出

for i in range(len(propagate_down)):

if not propagate_down[i]:

continue

bottom[i].diff[...] = top[i].diff[:]

# 其實還要乘以這個層的導數,但是由于y=x的導數是1.

# 所以無所謂了,直接把loss值一動不動的傳遞下來就好.

對于top和bottom在forward()和backward()函數中不同的意義,不要懵...

top[i].diff是從loss層返回來的梯度,以二分類為例,它的結構是兩列,一列是label 0的梯度,一列是label 1的梯度. 因此在backward()正常情況是需要把top[i].diff乘以self.diff的,也就是在forward()中算好的Python Layer自身的梯度. 然后賦值給bottom[i].diff,反向傳播到上一層.

關于propagate_down這個東西,我認為是定義是否在這個層做反向傳播(權值更新)的,也就是在遷移學習中,如果要固定不更新某一層的參數,就是用propagate_down來控制的. 不用管它,反正用默認的代碼就好了.

總的來說,要實現y=x這么一個層,需要寫的python代碼就是:

import caffe

class MyLayer(caffe.Layer):

def setup(self, bottom, top):

pass

def reshape(self, bottom, top):

top[0].reshape(*bottom[0].data.shape)

def forward(self, bottom, top):

top[0].data[...] = bottom[0].data[:]

def backward(self, self, top, propagate_down, bottom):

for i in range(len(propagate_down)):

if not propagate_down[i]:

continue

bottom[i].diff[...] = top[i].diff[:]

3. 結語

我認為要在Caffe中寫好一個Python Layer,最重要的是抓住兩點

1)處理好prototxt到python文件的參數傳遞

2)不能忘了在forward()中計算反向傳播梯度

接下來就是一些代碼理解和學術創新的事情了,懂得如何寫Python Layer,在運動Caffe的過程中就多開了一扇窗,從此不再只是調整solver.prototxt,還有在train_val.prototxt中組合卷積層/池化層/全連接/Residual Unit/Dense Unit這些低級的修改.

更多的細節可以參考最前面的幾個參考鏈接還有自己的理解實踐. 在實踐過程中,超級建議print所有你不了解的數據結構,例如forward()中的bottom,top; backward()中的bottom,top,即便Caffe用GPU加速,它也會給你打印出來你想要看的數據,一步一步的摸索數據的傳遞和存儲,這也是我花最多時間去弄明白的地方.

祝好!

總結

以上是生活随笔為你收集整理的多线程 python layer_在Caffe中加Python Layer的方法的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。