-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Python layer for rapidly writing nets in Python #1020
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ede8d58
d075e6f
9a09a28
ebf148b
a48990d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| #ifndef CAFFE_PYTHON_LAYER_HPP_ | ||
| #define CAFFE_PYTHON_LAYER_HPP_ | ||
|
|
||
| #include <boost/python.hpp> | ||
| #include <vector> | ||
|
|
||
| #include "../python/caffe/_caffe.hpp" | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Compilation] Remove this include. Put necessary things in python_layer.hpp instead of _caffe.hpp. |
||
| #include "caffe/layer.hpp" | ||
|
|
||
| namespace caffe { | ||
|
|
||
| /** | ||
| * @brief Wrap a layer implemented in Python. | ||
| */ | ||
| template <typename Dtype> | ||
| class PythonLayer : public Layer<Dtype> { | ||
| public: | ||
| /** | ||
| * @param param provides python_param, with required parameters: | ||
| * - module. The module to import with the layer implementation. Note that | ||
| * the current directory is not in the module search path by default. | ||
| * - layer. The name of the layer class, which must implement setup | ||
| * (for LayerSetUp), reshape (for Reshape), forward (for Forward_cpu), and | ||
| * backward (for Backward_cpu). | ||
| */ | ||
| explicit PythonLayer(const LayerParameter& param) | ||
| : Layer<Dtype>(param) {} | ||
| virtual void LayerSetUp(const vector<Blob<Dtype>*>& bottom, | ||
| const vector<Blob<Dtype>*>& top); | ||
| virtual void Reshape(const vector<Blob<Dtype>*>& bottom, | ||
| const vector<Blob<Dtype>*>& top); | ||
|
|
||
| virtual inline LayerParameter_LayerType type() const { | ||
| return LayerParameter_LayerType_PYTHON; | ||
| } | ||
|
|
||
| protected: | ||
| virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom, | ||
| const vector<Blob<Dtype>*>& top); | ||
| virtual void Backward_cpu(const vector<Blob<Dtype>*>& top, | ||
| const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom); | ||
|
|
||
| boost::python::object layer_; | ||
|
|
||
| private: | ||
| vector<PyBlob<Dtype> > PythonBlobVector(const vector<Blob<Dtype>*>& vec); | ||
| }; | ||
|
|
||
| } // namespace caffe | ||
|
|
||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,13 +18,21 @@ using boost::shared_ptr; | |
|
|
||
| namespace caffe { | ||
|
|
||
|
|
||
| // wrap shared_ptr<Blob> in a class that we construct in C++ and pass | ||
| // to Python | ||
| template <typename Dtype> | ||
| class PyBlob { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Compilation] As far as I understand it, PyBlob is what gets used in python_layer.hpp: we should remove it from _caffe.hpp, and put it in python_layer.hpp so libcaffe.a does not rely on _caffe.hpp or _caffe.cpp. If there is anything implemented in _caffe.cpp for PyBlob, move that to python_layer.cpp too.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (or if you would like things to be clearer, create caffe/util/python_util.hpp for PyBlob.) |
||
| public: | ||
| explicit PyBlob(const shared_ptr<Blob<Dtype> > &blob) | ||
| : blob_(blob) {} | ||
| // Construct from shared_ptr: memory will be correctly managed, | ||
| // even if Python holds onto a Blob beyond the life of its Net. | ||
| explicit PyBlob(const shared_ptr<Blob<Dtype> >& blob) | ||
| : blob_(blob) { } | ||
| // Construct from raw pointer: memory will become invalid once the | ||
| // owning Net is deleted. This exists only so that the raw Blob*s | ||
| // used in the layer interface can be passed to embedded Python. | ||
| explicit PyBlob(Blob<Dtype>* blob) | ||
| : blob_(blob, null_deleter()) { } | ||
|
|
||
| int num() const { return blob_->num(); } | ||
| int channels() const { return blob_->channels(); } | ||
|
|
@@ -42,6 +50,13 @@ class PyBlob { | |
|
|
||
| protected: | ||
| shared_ptr<Blob<Dtype> > blob_; | ||
|
|
||
| private: | ||
| // A dummy class that lets us use raw pointers as shared_ptrs to get | ||
| // around the fact that layers take around raw pointers. | ||
| struct null_deleter { | ||
| void operator()(void const*) const { } | ||
| }; | ||
| }; | ||
|
|
||
| // We need another wrapper (used as boost::python's HeldType) that receives a | ||
|
|
@@ -91,7 +106,6 @@ class PyNet { | |
|
|
||
| void Init(string param_file); | ||
|
|
||
|
|
||
| // Generate Python exceptions for badly shaped or discontiguous arrays. | ||
| inline void check_contiguous_array(PyArrayObject* arr, string name, | ||
| int channels, int height, int width); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,9 @@ | |
| #include "caffe/layer.hpp" | ||
| #include "caffe/proto/caffe.pb.h" | ||
| #include "caffe/vision_layers.hpp" | ||
| #ifdef USE_PYTHON_LAYER | ||
| #include "caffe/python_layer.hpp" | ||
| #endif | ||
|
|
||
| namespace caffe { | ||
|
|
||
|
|
@@ -231,6 +234,13 @@ Layer<Dtype>* GetLayer(const LayerParameter& param) { | |
| return GetPoolingLayer<Dtype>(name, param); | ||
| case LayerParameter_LayerType_POWER: | ||
| return new PowerLayer<Dtype>(param); | ||
| case LayerParameter_LayerType_PYTHON: | ||
| #ifdef USE_PYTHON_LAYER | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Compilation] Hmm, after you merge to the dev head there should be no more legacy factory code, no? Under the new factory code you wouldn't need to do ifdef anymore. If the code is not compiled with python layer, the code automatically tells you it's not available - that's the beauty of registraiton. |
||
| return new PythonLayer<Dtype>(param); | ||
| #else | ||
| LOG(FATAL) << "Attempt to use PythonLayer, but built without " | ||
| "USE_PYTHON_LAYER option."; | ||
| #endif | ||
| case LayerParameter_LayerType_RELU: | ||
| return GetReLULayer<Dtype>(name, param); | ||
| case LayerParameter_LayerType_SILENCE: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| #ifdef USE_PYTHON_LAYER | ||
| #include <boost/python.hpp> | ||
| #include <Python.h> | ||
| #include <vector> | ||
|
|
||
| #include "caffe/layer.hpp" | ||
| #include "caffe/python_layer.hpp" | ||
|
|
||
| namespace bp = boost::python; | ||
|
|
||
| namespace caffe { | ||
|
|
||
| template <typename Dtype> | ||
| vector<PyBlob<Dtype> > PythonLayer<Dtype>::PythonBlobVector( | ||
| const vector<Blob<Dtype>*>& vec) { | ||
| return vector<PyBlob<Dtype> >(vec.begin(), vec.end()); | ||
| } | ||
|
|
||
| template <typename Dtype> | ||
| void PythonLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom, | ||
| const vector<Blob<Dtype>*>& top) { | ||
| Py_Initialize(); | ||
| init_caffe(); | ||
|
|
||
| try { | ||
| bp::object module_ = bp::import( | ||
| this->layer_param_.python_param().module().c_str()); | ||
| layer_ = module_.attr(this->layer_param_.python_param().layer().c_str())(); | ||
|
|
||
| layer_.attr("setup")(PythonBlobVector(bottom), PythonBlobVector(top)); | ||
| } catch (bp::error_already_set) { | ||
| PyErr_Print(); | ||
| throw; | ||
| } | ||
| } | ||
|
|
||
| template <typename Dtype> | ||
| void PythonLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom, | ||
| const vector<Blob<Dtype>*>& top) { | ||
| try { | ||
| layer_.attr("reshape")(PythonBlobVector(bottom), PythonBlobVector(top)); | ||
| } catch (bp::error_already_set) { | ||
| PyErr_Print(); | ||
| throw; | ||
| } | ||
| } | ||
|
|
||
| template <typename Dtype> | ||
| void PythonLayer<Dtype>::Forward_cpu(const vector<Blob<Dtype>*>& bottom, | ||
| const vector<Blob<Dtype>*>& top) { | ||
| try { | ||
| layer_.attr("forward")(PythonBlobVector(bottom), PythonBlobVector(top)); | ||
| } catch (bp::error_already_set) { | ||
| PyErr_Print(); | ||
| throw; | ||
| } | ||
| } | ||
|
|
||
| template <typename Dtype> | ||
| void PythonLayer<Dtype>::Backward_cpu(const vector<Blob<Dtype>*>& top, | ||
| const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) { | ||
| try { | ||
| layer_.attr("backward")(PythonBlobVector(top), propagate_down, | ||
| PythonBlobVector(bottom)); | ||
| } catch (bp::error_already_set) { | ||
| PyErr_Print(); | ||
| throw; | ||
| } | ||
| } | ||
|
|
||
| INSTANTIATE_CLASS(PythonLayer); | ||
|
|
||
| } // namespace caffe | ||
| #endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Compilation] I think this causes the mutliple definition problem: python/caffe/_caffe.o is going to be linked into libcaffe.a because of this, and then when we make pycaffe, python/caffe/_caffe.cpp (which _caffe.o comes from) gets linked again - causing multiple definitions.
We should remove this line (together with other changes, see below).