caffe
커스텀 파이썬 레이어
수색…
소개
이 튜토리얼은 파이썬을 사용하여 Caffe를위한 간단한 사용자 정의 레이어를 만드는 단계를 안내합니다. 마지막으로 맞춤 레이어의 몇 가지 예가 있습니다. 일반적으로 사용자 정의 레이어를 만들어 Caffe에서 사용할 수없는 기능을 구현하고 요구 사항에 맞게 조정합니다.
파이썬 커스텀 레이어를 생성하면 네트워크에 약간의 오버 헤드가 추가되고 아마도 C ++ 커스텀 레이어만큼 효율적이지 않을 것입니다. 그러나 이런 식으로 전체 카페를 새로운 레이어로 컴파일 할 필요가 없습니다.
매개 변수
| 매개 변수 | 세부 |
|---|---|
| 상단 | 레이어의 위쪽 blob이있는 배열입니다. top [i] .data 를 사용하여 전달 된 데이터에 액세스합니다. 여기서 i 는 특정 BLOB의 인덱스입니다. |
| 바닥 | 레이어의 맨 아래에 blob이있는 배열입니다. bottom [i] .data 를 사용하여 전달 된 데이터에 액세스합니다. 여기서 i 는 특정 BLOB의 인덱스입니다. |
비고
- 파이썬 레이어로 Caffe 빌드
Caffe는 WITH_PYTHON_LAYER 옵션을 사용하여 컴파일해야합니다.
WITH_PYTHON_LAYER=1 make && make pycaffe
- 클래스 파일은 어디에 저장해야합니까?
두 가지 옵션이 있습니다 (적어도 내가 아는 것). Caffe 명령을 실행할 폴더와 동일한 폴더에 사용자 정의 계층 파일을 저장할 수 있습니다 (prototxt 파일이있는 위치 일 수도 있음). 또 다른 방법은, 내가 가장 좋아하는 방법은 폴더에 모든 사용자 지정 레이어를 저장하고이 폴더를 PYTHONPATH에 추가하는 것입니다.
참고 문헌
레이어 템플릿
import caffe
class My_Custom_Layer(caffe.Layer):
def setup(self, bottom, top):
pass
def forward(self, bottom, top):
pass
def reshape(self, bottom, top):
pass
def backward(self, bottom, top):
pass
기억해야 할 중요한 것들 :
- 커스텀 레이어는 caffe.Layer 로부터 상속 되어야합니다 (그래서 Caffe 를 임포트 하는 것을 잊지 마십시오);
- 다음 네 가지 방법을 정의해야합니다 : 설정 , 앞으로 , 다시 모양 및 뒤로 ;
- 모든 메소드에는 입력을 저장하는 blob 및 레이어로 전달되는 출력 인 top 및 bottom 매개 변수가 있습니다. 당신은 top [i] .data 또는 bottom [i] .data를 사용하여 접근 할 수 있습니다. 여기서 blob이 둘 이상인 경우에 나는 blob의 인덱스입니다.
- 설치 방법
Caffe가 모든 레이어를 인스턴스화 할 때 실행 시간 동안 Setup 메서드가 한 번 호출됩니다. 여기서 매개 변수를 읽고 고정 크기 버퍼를 인스턴스화합니다.
- 변형 방법
맨 아래 blob (레이어 입력) 크기에 따라 초기화 / 설정을위한 재 형성 방법을 사용하십시오. 네트워크가 인스턴스화 될 때 한 번 호출됩니다.
- 전달 방법
Forward 메서드는 각 입력 배치에 대해 호출되며 대부분의 논리가됩니다.
- 이전 방법
Backward 메서드는 네트워크의 역방향 통과 중에 호출됩니다. 예를 들어, convolution-like 레이어에서 이것은 그라디언트를 계산할 위치입니다. 이 옵션은 선택 사항입니다 (레이어는 전달 전용 일 수 있음).
Prototxt 템플릿
이제는 레이어 디자인이되었습니다. 이것은 .prototxt 파일에서 정의하는 방법입니다.
layer {
name: "LayerName"
type: "Python"
top: "TopBlobName"
bottom: "BottomBlobName"
python_param {
module: "My_Custom_Layer_File"
layer: "My_Custom_Layer_Class"
param_str: '{"param1": 1,"param2":True, "param3":"some string"}'
}
include{
phase: TRAIN
}
}
중요한 발언 :
- type 은 Python 이어야합니다.
- 적어도 모듈 과 레이어 매개 변수를 가진 python_param 사전이 있어야합니다.
- 모듈 은 ( .py 없이) 레이어를 구현 한 파일을 참조합니다.
- layer 는 클래스 이름을 나타냅니다.
- param_str을 사용하여 레이어에 매개 변수를 전달할 수 있습니다 (더 자세히 살펴보면 액세스 할 수 있습니다).
- 다른 레이어와 마찬가지로 활성화 할 위상을 정의 할 수 있습니다 (예제를 통해 현재 위상을 확인할 수있는 방법 참조).
레이어에 매개 변수 전달
param_str을 사용하여 prototxt 에서 레이어 매개 변수를 정의 할 수 있습니다. 이 작업을 완료하면 레이어 클래스 내에서 다음과 같은 매개 변수에 액세스하는 방법에 대한 예가 있습니다.
def setup(self, bottom, top):
params = eval(self.param_str)
param1 = params["param1"]
param2 = params.get('param2', False) #I usually use this when fetching a bool
param3 = params["param3"]
#Continue with the setup
# ...
레이어 측정
이 예에서는 훈련 중 이진 문제에 대한 정확도와 혼동 행렬을 출력하는 "측정"계층과 테스트 / 유효성 검사 중에 정확도, 오 탐지율 및 위음성 비율을 설계합니다. Caffe에는 이미 정확도 레이어가 있지만 때때로 F 측정과 같은 더 많은 것을 원합니다.
내 클래스 정의가있는 내 measureLayer.py 입니다.
#Remark: This class is designed for a binary problem, where the first class would be the 'negative'
# and the second class would be 'positive'
import caffe
TRAIN = 0
TEST = 1
class Measure_Layer(caffe.Layer):
#Setup method
def setup(self, bottom, top):
#We want two bottom blobs, the labels and the predictions
if len(bottom) != 2:
raise Exception("Wrong number of bottom blobs (prediction and label)")
#And some top blobs, depending on the phase
if self.phase = TEST and len(top) != 3:
raise Exception("Wrong number of top blobs (acc, FPR, FNR)")
if self.phase = TRAIN and len(top) != 5:
raise Exception("Wrong number of top blobs (acc, tp, tn, fp and fn)")
#Initialize some attributes
self.TPs = 0.0
self.TNs = 0.0
self.FPs = 0.0
self.FNs = 0.0
self.totalImgs = 0
#Forward method
def forward(self, bottom, top):
#The order of these depends on the prototxt definition
predictions = bottom[0].data
labels = bottom[1].data
self.totalImgs += len(labels)
for i in range(len(labels)): #len(labels) is equal to the batch size
pred = predictions[i] #pred is a tuple with the normalized probability
#of a sample i.r.t. two classes
lab = labels[i]
if pred[0] > pred[1]:
if lab == 1.0:
self.FNs += 1.0
else:
self.TNs += 1.0
else:
if lab == 1.0:
self.TPs += 1.0
else:
self.FPs += 1.0
acc = (self.TPs + self.TNs) / self.totalImgs
try: #just assuring we don't divide by 0
fpr = self.FPs / (self.FPs + self.TNs)
except:
fpr = -1.0
try: #just assuring we don't divide by 0
fnr = self.FNs / (self.FNs + self.TPs)
except:
fnr = -1.0
#output data to top blob
top[0].data = acc
if self.phase == TRAIN:
top[1].data = self.TPs
top[2].data = self.TNs
top[3].data = self.FPs
top[4].data = self.FNs
elif self.phase == TEST:
top[1].data = fpr
top[2].data = fnr
def reshape(self, bottom, top):
"""
We don't need to reshape or instantiate anything that is input-size sensitive
"""
pass
def backward(self, bottom, top):
"""
This layer does not back propagate
"""
pass
그리고 이것은 그것의 prototxt 의 예입니다 :
layer {
name: "metrics"
type: "Python"
top: "Acc"
top: "TPs"
top: "TNs"
top: "FPs"
top: "FNs"
bottom: "prediction" #let's supose we have these two bottom blobs
bottom: "label"
python_param {
module: "measureLayer"
layer: "Measure_Layer"
}
include {
phase: TRAIN
}
}
layer {
name: "metrics"
type: "Python"
top: "Acc"
top: "FPR"
top: "FNR"
bottom: "prediction" #let's supose we have these two bottom blobs
bottom: "label"
python_param {
module: "measureLayer"
layer: "Measure_Layer"
}
include {
phase: TEST
}
}
데이터 영역
이 예제는 이미지 경로가있는 텍스트 파일을 받고, 일괄 처리 이미지를로드하고, 미리 처리하는 맞춤 데이터 영역입니다. 빠른 팁, Caffe는 이미 많은 데이터 영역을 가지고 있으며 사용자 정의 레이어는 단순한 무언가를 원할 경우 가장 효율적인 방법은 아닙니다.
내 dataLayer.py 는 다음과 같을 수 있습니다.
import caffe
class Custom_Data_Layer(caffe.Layer):
def setup(self, bottom, top):
# Check top shape
if len(top) != 2:
raise Exception("Need to define tops (data and label)")
#Check bottom shape
if len(bottom) != 0:
raise Exception("Do not define a bottom.")
#Read parameters
params = eval(self.param_str)
src_file = params["src_file"]
self.batch_size = params["batch_size"]
self.im_shape = params["im_shape"]
self.crop_size = params.get("crop_size", False)
#Reshape top
if self.crop_size:
top[0].reshape(self.batch_size, 3, self.crop_size, self.crop_size)
else:
top[0].reshape(self.batch_size, 3, self.im_shape, self.im_shape)
top[1].reshape(self.batch_size)
#Read source file
#I'm just assuming we have this method that reads the source file
#and returns a list of tuples in the form of (img, label)
self.imgTuples = readSrcFile(src_file)
self._cur = 0 #use this to check if we need to restart the list of imgs
def forward(self, bottom, top):
for itt in range(self.batch_size):
# Use the batch loader to load the next image.
im, label = self.load_next_image()
#Here we could preprocess the image
# ...
# Add directly to the top blob
top[0].data[itt, ...] = im
top[1].data[itt, ...] = label
def load_next_img(self):
#If we have finished forwarding all images, then an epoch has finished
#and it is time to start a new one
if self._cur == len(self.imgTuples):
self._cur = 0
shuffle(self.imgTuples)
im, label = self.imgTuples[self._cur]
self._cur += 1
return im, label
def reshape(self, bottom, top):
"""
There is no need to reshape the data, since the input is of fixed size
(img shape and batch size)
"""
pass
def backward(self, bottom, top):
"""
This layer does not back propagate
"""
pass
그리고 prototxt 는 다음과 같습니다.
layer {
name: "Data"
type: "Python"
top: "data"
top: "label"
python_param {
module: "dataLayer"
layer: "Custom_Data_Layer"
param_str: '{"batch_size": 126,"im_shape":256, "crop_size":224, "src_file": "path_to_TRAIN_file.txt"}'
}
}