Skip to content

Commit

Permalink
pycaffe update
Browse files Browse the repository at this point in the history
  • Loading branch information
Yangqing committed Nov 13, 2013
1 parent 26f5a14 commit b4fa660
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 24 deletions.
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ all: $(NAME) $(STATIC_NAME) test examples pycaffe

pycaffe: $(STATIC_NAME) python/caffe/pycaffe.cpp $(PROTO_GEN_PY)
protoc --proto_path=src --python_out=python $(PROTO_SRCS)
$(CXX) -o python/caffe/pycaffe.so -I/usr/include/python2.7 -shared \
$(CXX) -o python/caffe/pycaffe.so -I/usr/include/python2.7 \
-I/usr/local/lib/python2.7/dist-packages/numpy/core/include/ -shared \
python/caffe/pycaffe.cpp $(STATIC_NAME) $(CXXFLAGS) $(LDFLAGS) \
$(WARNING) -lboost_python -lpython2.7

Expand Down
22 changes: 17 additions & 5 deletions include/caffe/net.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,15 @@ class Net {
void Init(const NetParameter& param,
const vector<Blob<Dtype>* >& bottom);

// Run forward with the input blobs already fed separately. You can get the
// input blobs using input_blobs().
const vector<Blob<Dtype>*>& ForwardPrefilled();
// Run forward using a set of bottom blobs, and return the result.
const vector<Blob<Dtype>*>& Forward(const vector<Blob<Dtype>* > & bottom);
// Run forward using a serialized BlobProtoVector and return the result
// as a serialized BlobProtoVector
string Forward(const string& input_blob_protos);

// The network backward should take no input and output, since it solely
// computes the gradient w.r.t the parameters, and the data has already
// been provided during the forward pass.
Expand All @@ -48,6 +52,9 @@ class Net {
return Backward();
}

// Updates the network weights based on the diff values computed.
void Update();

// For an already initialized net, CopyTrainedLayersFrom() copies the already
// trained layers from another net parameter instance.
void CopyTrainedLayersFrom(const NetParameter& param);
Expand All @@ -74,8 +81,11 @@ class Net {
// returns the parameter learning rate multipliers
inline vector<float>& params_lr() {return params_lr_; }
inline vector<float>& params_weight_decay() { return params_weight_decay_; }
// Updates the network
void Update();
// Input and output blob numbers
inline int num_inputs() { return net_input_blobs_.size(); }
inline int num_outputs() { return net_output_blobs_.size(); }
inline vector<Blob<Dtype>*>& input_blobs() { return net_input_blobs_; }
inline vector<Blob<Dtype>*>& output_blobs() { return net_output_blobs_; }

protected:
// Function to get misc parameters, e.g. the learning rate multiplier and
Expand All @@ -91,15 +101,17 @@ class Net {
vector<shared_ptr<Blob<Dtype> > > blobs_;
vector<string> blob_names_;
vector<bool> blob_need_backward_;
// bottom_vecs stores the vectors containing the input for each layer
// bottom_vecs stores the vectors containing the input for each layer.
// They don't actually host the blobs (blobs_ does), so we simply store
// pointers.
vector<vector<Blob<Dtype>*> > bottom_vecs_;
vector<vector<int> > bottom_id_vecs_;
// top_vecs stores the vectors containing the output for each layer
vector<vector<Blob<Dtype>*> > top_vecs_;
vector<vector<int> > top_id_vecs_;
// blob indices for the input and the output of the net.
// blob indices for the input and the output of the net
vector<int> net_input_blob_indices_;
vector<int> net_output_blob_indices_;
vector<Blob<Dtype>*> net_input_blobs_;
vector<Blob<Dtype>*> net_output_blobs_;
string name_;
// The parameters in the network.
Expand Down
17 changes: 17 additions & 0 deletions python/caffe/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,23 @@ def array_to_blobproto(arr, diff=None):
return blob


def arraylist_to_blobprotovecor_str(arraylist):
"""Converts a list of arrays to a serialized blobprotovec, which could be
then passed to a network for processing.
"""
vec = caffe_pb2.BlobProtoVector()
vec.blobs.extend([array_to_blobproto(arr) for arr in arraylist])
return vec.SerializeToString()


def blobprotovector_str_to_arraylist(str):
"""Converts a serialized blobprotovec to a list of arrays.
"""
vec = caffe_pb2.BlobProtoVector()
vec.ParseFromString(str)
return [blobproto_to_array(blob) for blob in vec.blobs]


def array_to_datum(arr, label=0):
"""Converts a 3-dimensional array to datum. If the array has dtype uint8,
the output data will be encoded as a string. Otherwise, the output data
Expand Down
69 changes: 64 additions & 5 deletions python/caffe/pycaffe.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
// Copyright Yangqing Jia 2013
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION

#include <boost/python.hpp>
#include <numpy/arrayobject.h>
#include "caffe/caffe.hpp"


using namespace caffe;
using namespace boost::python;

// For python, we will simply use float. This is wrapped in a
#define PTYPE float

// For python, we will simply use float.
// A simple wrapper over CaffeNet that runs the forward process.
struct CaffeNet
{
Expand All @@ -20,8 +23,64 @@ struct CaffeNet
net_->CopyTrainedLayersFrom(pretrained_param_file);
}

string Forward(const string& input_blobs) {
return net_->Forward(input_blobs);
virtual ~CaffeNet() {}

void Forward(boost::python::list bottom, boost::python::list top) {
vector<Blob<float>*>& input_blobs = net_->input_blobs();
CHECK_EQ(boost::python::len(bottom), input_blobs.size());
CHECK_EQ(boost::python::len(top), net_->num_outputs());
// First, copy the input
for (int i = 0; i < input_blobs.size(); ++i) {
boost::python::object elem = bottom[i];
PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(elem.ptr());
CHECK_EQ(PyArray_NDIM(arr), 4);
CHECK_EQ(PyArray_ITEMSIZE(arr), 4);
npy_intp* dims = PyArray_DIMS(arr);
CHECK_EQ(dims[0], input_blobs[i]->num());
CHECK_EQ(dims[1], input_blobs[i]->channels());
CHECK_EQ(dims[2], input_blobs[i]->height());
CHECK_EQ(dims[3], input_blobs[i]->width());
switch (Caffe::mode()) {
case Caffe::CPU:
memcpy(input_blobs[i]->mutable_cpu_data(), PyArray_DATA(arr),
sizeof(float) * input_blobs[i]->count());
break;
case Caffe::GPU:
cudaMemcpy(input_blobs[i]->mutable_gpu_data(), PyArray_DATA(arr),
sizeof(float) * input_blobs[i]->count(), cudaMemcpyHostToDevice);
break;
default:
LOG(FATAL) << "Unknown Caffe mode.";
} // switch (Caffe::mode())
}
LOG(INFO) << "Start";
const vector<Blob<float>*>& output_blobs = net_->ForwardPrefilled();
LOG(INFO) << "End";
for (int i = 0; i < output_blobs.size(); ++i) {
boost::python::object elem = top[i];
PyArrayObject* arr = reinterpret_cast<PyArrayObject*>(elem.ptr());
CHECK(PyArray_FLAGS(arr) & NPY_ARRAY_C_CONTIGUOUS);
CHECK_EQ(PyArray_NDIM(arr), 4);
CHECK_EQ(PyArray_ITEMSIZE(arr), 4);
npy_intp* dims = PyArray_DIMS(arr);
CHECK_EQ(dims[0], output_blobs[i]->num());
CHECK_EQ(dims[1], output_blobs[i]->channels());
CHECK_EQ(dims[2], output_blobs[i]->height());
CHECK_EQ(dims[3], output_blobs[i]->width());
switch (Caffe::mode()) {
case Caffe::CPU:
memcpy(PyArray_DATA(arr), output_blobs[i]->cpu_data(),
sizeof(float) * output_blobs[i]->count());
break;
case Caffe::GPU:
cudaMemcpy(PyArray_DATA(arr), output_blobs[i]->gpu_data(),
sizeof(float) * output_blobs[i]->count(), cudaMemcpyDeviceToHost);
break;
default:
LOG(FATAL) << "Unknown Caffe mode.";
} // switch (Caffe::mode())

}
}

void set_mode_cpu() { Caffe::set_mode(Caffe::CPU); }
Expand Down
29 changes: 16 additions & 13 deletions src/caffe/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ void Net<Dtype>::Init(const NetParameter& param,
blob_names_.push_back(blob_name);
blob_need_backward_.push_back(false);
net_input_blob_indices_.push_back(i);
net_input_blobs_.push_back(blob_pointer.get());
blob_name_to_idx[blob_name] = i;
available_blobs.insert(blob_name);
}
Expand Down Expand Up @@ -174,7 +175,6 @@ void Net<Dtype>::Init(const NetParameter& param,
for (set<string>::iterator it = available_blobs.begin();
it != available_blobs.end(); ++it) {
LOG(INFO) << "This network produces output " << *it;
net_output_blob_indices_.push_back(blob_name_to_idx[*it]);
net_output_blobs_.push_back(blobs_[blob_name_to_idx[*it]].get());
}
GetLearningRateAndWeightDecay();
Expand Down Expand Up @@ -221,34 +221,37 @@ void Net<Dtype>::GetLearningRateAndWeightDecay() {
}

template <typename Dtype>
const vector<Blob<Dtype>*>& Net<Dtype>::Forward(
const vector<Blob<Dtype>*> & bottom) {
// Copy bottom to internal bottom
for (int i = 0; i < bottom.size(); ++i) {
blobs_[net_input_blob_indices_[i]]->CopyFrom(*bottom[i]);
}
const vector<Blob<Dtype>*>& Net<Dtype>::ForwardPrefilled() {
for (int i = 0; i < layers_.size(); ++i) {
// LOG(ERROR) << "Forwarding " << layer_names_[i];
layers_[i]->Forward(bottom_vecs_[i], &top_vecs_[i]);
}
return net_output_blobs_;
}

template <typename Dtype>
const vector<Blob<Dtype>*>& Net<Dtype>::Forward(
const vector<Blob<Dtype>*> & bottom) {
// Copy bottom to internal bottom
for (int i = 0; i < bottom.size(); ++i) {
net_input_blobs_[i]->CopyFrom(*bottom[i]);
}
return ForwardPrefilled();
}


template <typename Dtype>
string Net<Dtype>::Forward(const string& input_blob_protos) {
BlobProtoVector blob_proto_vec;
if (net_input_blob_indices_.size()) {
if (net_input_blobs_.size()) {
blob_proto_vec.ParseFromString(input_blob_protos);
CHECK_EQ(blob_proto_vec.blobs_size(), net_input_blob_indices_.size())
CHECK_EQ(blob_proto_vec.blobs_size(), net_input_blobs_.size())
<< "Incorrect input size.";
for (int i = 0; i < blob_proto_vec.blobs_size(); ++i) {
blobs_[net_input_blob_indices_[i]]->FromProto(blob_proto_vec.blobs(i));
net_input_blobs_[i]->FromProto(blob_proto_vec.blobs(i));
}
}
for (int i = 0; i < layers_.size(); ++i) {
layers_[i]->Forward(bottom_vecs_[i], &top_vecs_[i]);
}
ForwardPrefilled();
blob_proto_vec.Clear();
for (int i = 0; i < net_output_blobs_.size(); ++i) {
net_output_blobs_[i]->ToProto(blob_proto_vec.add_blobs());
Expand Down

0 comments on commit b4fa660

Please sign in to comment.