caffe 中的 `"Python"` 层是什么?

What is a `"Python"` layer in caffe?

Caffe 有层类型 "Python"

例如,此图层类型可用作 loss layer
在其他情况下,它被用作 .

这个图层类型是什么?
这个图层怎么用?

非常简单,它是提供实现代码的层,而不是使用其中一种预定义类型——它们都由高效函数支持。

如果要定义自定义损失函数,请继续:自己编写,并创建类型为 Python 的图层。如果您有非标准的输入需求,也许是一些特定于数据的预处理,没问题:自己编写,并创建类型为 Python.[=10 的层=]

Python层不同于需要编译的C++层,它们的参数需要添加到proto文件中,最后需要在layer_factory中注册层。如果你写一个 python 层,你不需要担心这些事情。层参数可以定义为字符串,在python中可以作为字符串访问。例如:如果您在层中有一个参数,您可以使用 'self.param_str' 访问它,如果 param_str 在您的 prototxt 文件中定义。和其他图层一样,需要定义一个class,函数如下:

  • 设置 - 使用从图层变量获得的参数初始化图层
  • Forward - 层的输入和输出是什么
  • 向后 - 给定下一层的预测和梯度,计算上一层的梯度
  • Reshape - 如果需要,重塑你的 blob

Prototxt 示例:

layer {
  name: 'rpn-data'
  type: 'Python'
  bottom: 'rpn_cls_score'
  bottom: 'gt_boxes'
  bottom: 'im_info'
  bottom: 'data'
  top: 'rpn_labels'
  top: 'rpn_bbox_targets'
  top: 'rpn_bbox_inside_weights'
  top: 'rpn_bbox_outside_weights'
  python_param {
    module: 'rpn.anchor_target_layer'
    layer: 'AnchorTargetLayer'
    param_str: "'feat_stride': 16"
  }
}

这里层的名称是rpn-data,bottom和top分别是层的输入和输出细节。 python_param 定义了 Python 层的参数。 'module' 指定图层的文件名。如果名为 'anchor_target_layer.py' 的文件位于名为 'rpn' 的文件夹内,则参数将为 'rpn.anchor_target_layer'。 'layer' 参数是您的 class 的名称,在本例中为 'AnchorTargetLayer'。 'param_str' 是层的参数,其中包含键值 16 'feat_stride'。

与 C++/CUDA 层不同,Python 层目前无法在 caffe 的多 GPU 设置中工作,因此这是使用它们的缺点。

's and 的答案给出了 "Python" 层的总体目的:一个通用层,用 python 而不是 c++ 实现。

我打算将此答案作为使用 "Python" 图层的教程。


"Python"层教程

什么是 "Python" 层?

请看 and 的优秀回答。

先决条件

为了使用'Python"层你需要用flag

编译caffe
WITH_PYTHON_LAYER := 1

设置在'Makefile.config'.

如何实现"Python"层?

"Python" 层应作为 python class 派生自 caffe.Layer 基础 class 来实现。这个class必须有以下四种方法:

import caffe
class my_py_layer(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

这些方法是什么?

def setup(self, bottom, top):该方法在caffe构建网络时调用一次。此函数应检查输入数 (len(bottom)) 和输出数 (len(top)) 是否符合预期。
您还应该在此处分配网络的内部参数(即 self.add_blobs()),有关详细信息,请参阅 this thread
此方法可以访问 self.param_str - 从 prototxt 传递到图层的字符串。有关详细信息,请参阅

def reshape(self, bottom, top):每当caffe重塑网络时调用该方法。此函数应分配输出(每个 top blob)。输出的形状通常与 bottoms 的形状有关。

def forward(self, bottom, top):实现从bottomtop的前向传递。

def backward(self, top, propagate_down, bottom):这个方法实现了反向传播,它将梯度从top传播到bottompropagate_downlen(bottom) 的布尔向量,指示应将梯度传播到 bottom 中的哪个。

有关 bottomtop 输入的更多信息,您可以在 中找到。

例子
您可以看到一些简化的 python 层 here, and here.
示例 可以找到 "moving average" 输出层的示例

可训练参数
"Python"层可以有可训练的参数(如"Conv""InnerProduct"等)。
您可以在 and . There's also a very simplified example in caffe git.

中找到有关添加可训练参数的更多信息

如何在 prototxt 中添加 "Python" 图层?

详见的回答。
您需要将以下内容添加到您的 prototxt 中:

layer {
  name: 'rpn-data'
  type: 'Python'  
  bottom: 'rpn_cls_score'
  bottom: 'gt_boxes'
  bottom: 'im_info'
  bottom: 'data'
  top: 'rpn_labels'
  top: 'rpn_bbox_targets'
  top: 'rpn_bbox_inside_weights'
  top: 'rpn_bbox_outside_weights'
  python_param {
    module: 'rpn.anchor_target_layer'  # python module name where your implementation is
    layer: 'AnchorTargetLayer'   # the name of the class implementation
    param_str: "'feat_stride': 16"   # optional parameters to the layer
  }
}

如何使用pythonic NetSpec接口添加"Python"层?

很简单:

import caffe
from caffe import layers as L

ns = caffe.NetSpec()
# define layers here...
ns.rpn_labels, ns.rpn_bbox_targets, \
  ns.rpn_bbox_inside_weights, ns.rpn_bbox_outside_weights = \
    L.Python(ns.rpn_cls_score, ns.gt_boxes, ns.im_info, ns.data, 
             name='rpn-data',
             ntop=4, # tell caffe to expect four output blobs
             python_param={'module': 'rpn.anchor_target_layer',
                           'layer': 'AnchorTargetLayer',
                           'param_str': '"\'feat_stride\': 16"'})

如何使用带有 "Python" 层的网络?

从 caffe 调用 python 代码是您无需担心的。 Caffe 使用 boost API 从已编译的 c++ 中调用 python 代码。
你需要做什么?
确保实现层的 python 模块在 $PYTHONPATH 中,这样当 caffe imports 它时 - 它可以被找到。
例如,如果您的模块 my_python_layer.py/path/to/my_python_layer.py 中,那么

PYTHONPATH=/path/to:$PYTHONPATH $CAFFE_ROOT/build/tools/caffe train -solver my_solver.prototxt

应该可以正常工作。

如何测试我的图层?

您应该始终在使用图层之前对其进行测试。
测试 forward 功能完全取决于您,因为每一层都有不同的功能。
测试backward方法是easy,因为这个方法只实现了forward的梯度,所以可以自动进行数值测试!
查看 test_gradient_for_python_layer 测试实用程序:

import numpy as np
from test_gradient_for_python_layer import test_gradient_for_python_layer

# set the inputs
input_names_and_values = [('in_cont', np.random.randn(3,4)), 
                          ('in_binary', np.random.binomial(1, 0.4, (3,1))]
output_names = ['out1', 'out2']
py_module = 'folder.my_layer_module_name'
py_layer = 'my_layer_class_name'
param_str = 'some params'
propagate_down = [True, False]

# call the test
test_gradient_for_python_layer(input_names_and_values, output_names, 
                               py_module, py_layer, param_str, 
                               propagate_down)

# you are done!

特别通知

值得注意的是 python 代码仅在 CPU 上运行。因此,如果您计划在网络的 中间 有一个 Python 层,如果您计划使用 GPU。发生这种情况是因为 caffe 需要在调用 python 层之前将 blob 从 GPU 复制到 CPU,然后复制回 GPU 以继续进行 forward/backward 传递。
如果 python 层是输入层或最顶层的损失层,这种退化就不那么明显了。
更新: 2017 年 9 月 19 日 PR #5904 被合并到 master 中。此 PR 通过 python 接口公开 blob 的 GPU 指针。 您可以直接从 python 访问 blob._gpu_data_ptr 和 blob._gpu_diff_ptr,风险自负.