Skip to content

Commit 21dae31

Browse files
committedMay 4, 2016
Merge remote-tracking branch 'bvlc/master' into caffe-0.15
Conflicts: examples/mnist/convert_mnist_data.cpp python/caffe/_caffe.cpp
2 parents f72de55 + 3d41c8a commit 21dae31

32 files changed

+386
-182
lines changed
 

‎Makefile

+4-4
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ endif
281281
ifeq ($(OSX), 1)
282282
CXX := /usr/bin/clang++
283283
ifneq ($(CPU_ONLY), 1)
284-
CUDA_VERSION := $(shell $(CUDA_DIR)/bin/nvcc -V | grep -o 'release \d' | grep -o '\d')
284+
CUDA_VERSION := $(shell $(CUDA_DIR)/bin/nvcc -V | grep -o 'release [0-9.]*' | grep -o '[0-9.]*')
285285
ifeq ($(shell echo | awk '{exit $(CUDA_VERSION) < 7.0;}'), 1)
286286
CXXFLAGS += -stdlib=libstdc++
287287
LINKFLAGS += -stdlib=libstdc++
@@ -383,9 +383,9 @@ ifeq ($(BLAS), mkl)
383383
# MKL
384384
LIBRARIES += mkl_rt
385385
COMMON_FLAGS += -DUSE_MKL
386-
MKL_DIR ?= /opt/intel/mkl
387-
BLAS_INCLUDE ?= $(MKL_DIR)/include
388-
BLAS_LIB ?= $(MKL_DIR)/lib $(MKL_DIR)/lib/intel64
386+
MKLROOT ?= /opt/intel/mkl
387+
BLAS_INCLUDE ?= $(MKLROOT)/include
388+
BLAS_LIB ?= $(MKLROOT)/lib $(MKLROOT)/lib/intel64
389389
else ifeq ($(BLAS), open)
390390
# OpenBLAS
391391
LIBRARIES += openblas

‎docker/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ docker_files: standalone_files
2222

2323
standalone_files: standalone/cpu/Dockerfile standalone/gpu/Dockerfile
2424

25-
FROM_GPU = "nvidia/cuda:cudnn"
25+
FROM_GPU = "nvidia/cuda:7.5-cudnn4-devel-ubuntu14.04"
2626
FROM_CPU = "ubuntu:14.04"
2727
GPU_CMAKE_ARGS = -DUSE_CUDNN=1
2828
CPU_CMAKE_ARGS = -DCPU_ONLY=1

‎docker/standalone/gpu/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM nvidia/cuda:cudnn
1+
FROM nvidia/cuda:7.5-cudnn4-devel-ubuntu14.04
22
MAINTAINER caffe-maint@googlegroups.com
33

44
RUN apt-get update && apt-get install -y --no-install-recommends \

‎docs/installation.md

+19-13
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,23 @@ title: Installation
55
# Installation
66

77
Prior to installing, have a glance through this guide and take note of the details for your platform.
8-
We install and run Caffe on Ubuntu 14.04 and 12.04, OS X 10.10 / 10.9 / 10.8, and AWS.
9-
The official Makefile and `Makefile.config` build are complemented by an automatic CMake build from the community.
8+
We install and run Caffe on Ubuntu 16.04–12.04, OS X 10.11–10.8, and through Docker and AWS.
9+
The official Makefile and `Makefile.config` build are complemented by a [community CMake build](#cmake-build).
10+
11+
**Step-by-step Instructions**:
12+
13+
- [Docker setup](https://github.com/BVLC/caffe/tree/master/docker) *out-of-the-box brewing*
14+
- [Ubuntu installation](install_apt.html) *the standard platform*
15+
- [OS X installation](install_osx.html)
16+
- [RHEL / CentOS / Fedora installation](install_yum.html)
17+
- [Windows](https://github.com/BVLC/caffe/tree/windows) *see the Windows branch led by Microsoft*
18+
- [OpenCL](https://github.com/BVLC/caffe/tree/opencl) *see the OpenCL branch led by Fabian Tschopp*
19+
20+
**Overview**:
1021

1122
- [Prerequisites](#prerequisites)
1223
- [Compilation](#compilation)
1324
- [Hardware](#hardware)
14-
- Platforms: [Ubuntu guide](install_apt.html), [OS X guide](install_osx.html), and [RHEL / CentOS / Fedora guide](install_yum.html)
1525

1626
When updating Caffe, it's best to `make clean` before re-compiling.
1727

@@ -20,7 +30,7 @@ When updating Caffe, it's best to `make clean` before re-compiling.
2030
Caffe has several dependencies:
2131

2232
* [CUDA](https://developer.nvidia.com/cuda-zone) is required for GPU mode.
23-
* library version 7.0 and the latest driver version are recommended, but 6.* is fine too
33+
* library version 7+ and the latest driver version are recommended, but 6.* is fine too
2434
* 5.5, and 5.0 are compatible but considered legacy
2535
* [BLAS](http://en.wikipedia.org/wiki/Basic_Linear_Algebra_Subprograms) via ATLAS, MKL, or OpenBLAS.
2636
* [Boost](http://www.boost.org/) >= 1.55
@@ -30,14 +40,14 @@ Optional dependencies:
3040

3141
* [OpenCV](http://opencv.org/) >= 2.4 including 3.0
3242
* IO libraries: `lmdb`, `leveldb` (note: leveldb requires `snappy`)
33-
* cuDNN for GPU acceleration (v3)
43+
* cuDNN for GPU acceleration (v4)
3444

3545
Pycaffe and Matcaffe interfaces have their own natural needs.
3646

3747
* For Python Caffe: `Python 2.7` or `Python 3.3+`, `numpy (>= 1.7)`, boost-provided `boost.python`
3848
* For MATLAB Caffe: MATLAB with the `mex` compiler.
3949

40-
**cuDNN Caffe**: for fastest operation Caffe is accelerated by drop-in integration of [NVIDIA cuDNN](https://developer.nvidia.com/cudnn). To speed up your Caffe models, install cuDNN then uncomment the `USE_CUDNN := 1` flag in `Makefile.config` when installing Caffe. Acceleration is automatic. The current version is cuDNN v3; older versions are supported in older Caffe.
50+
**cuDNN Caffe**: for fastest operation Caffe is accelerated by drop-in integration of [NVIDIA cuDNN](https://developer.nvidia.com/cudnn). To speed up your Caffe models, install cuDNN then uncomment the `USE_CUDNN := 1` flag in `Makefile.config` when installing Caffe. Acceleration is automatic. The current version is cuDNN v4; older versions are supported in older Caffe.
4151

4252
**CPU-only Caffe**: for cold-brewed CPU-only Caffe uncomment the `CPU_ONLY := 1` flag in `Makefile.config` to configure and build Caffe without CUDA. This is helpful for cloud or cluster deployment.
4353

@@ -82,10 +92,6 @@ Install MATLAB, and make sure that its `mex` is in your `$PATH`.
8292

8393
*Caffe's MATLAB interface works with versions 2015a, 2014a/b, 2013a/b, and 2012b.*
8494

85-
#### Windows
86-
87-
There is an unofficial Windows port of Caffe at [niuzhiheng/caffe:windows](https://github.com/niuzhiheng/caffe). Thanks [@niuzhiheng](https://github.com/niuzhiheng)!
88-
8995
## Compilation
9096

9197
Caffe can be compiled with either Make or CMake. Make is officially supported while CMake is supported by the community.
@@ -113,7 +119,7 @@ Be sure to set your MATLAB and Python paths in `Makefile.config` first!
113119

114120
Now that you have installed Caffe, check out the [MNIST tutorial](gathered/examples/mnist.html) and the [reference ImageNet model tutorial](gathered/examples/imagenet.html).
115121

116-
### Compilation with CMake
122+
### CMake Build
117123

118124
In lieu of manually editing `Makefile.config` to configure the build, Caffe offers an unofficial CMake build thanks to @Nerei, @akosiorek, and other members of the community. It requires CMake version >= 2.8.7.
119125
The basic steps are as follows:
@@ -129,9 +135,9 @@ See [PR #1667](https://github.com/BVLC/caffe/pull/1667) for options and details.
129135

130136
## Hardware
131137

132-
**Laboratory Tested Hardware**: Berkeley Vision runs Caffe with K40s, K20s, and Titans including models at ImageNet/ILSVRC scale. We also run on GTX series cards (980s and 770s) and GPU-equipped MacBook Pros. We have not encountered any trouble in-house with devices with CUDA capability >= 3.0. All reported hardware issues thus-far have been due to GPU configuration, overheating, and the like.
138+
**Laboratory Tested Hardware**: Berkeley Vision runs Caffe with Titan Xs, K80s, GTX 980s, K40s, K20s, Titans, and GTX 770s including models at ImageNet/ILSVRC scale. We have not encountered any trouble in-house with devices with CUDA capability >= 3.0. All reported hardware issues thus-far have been due to GPU configuration, overheating, and the like.
133139

134-
**CUDA compute capability**: devices with compute capability <= 2.0 may have to reduce CUDA thread numbers and batch sizes due to hardware constraints. Your mileage may vary.
140+
**CUDA compute capability**: devices with compute capability <= 2.0 may have to reduce CUDA thread numbers and batch sizes due to hardware constraints. Brew with caution; we recommend compute capability >= 3.0.
135141

136142
Once installed, check your times against our [reference performance numbers](performance_hardware.html) to make sure everything is configured properly.
137143

‎examples/cifar10/convert_cifar_data.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ void convert_dataset(const string& input_folder, const string& output_folder,
9191
}
9292

9393
int main(int argc, char** argv) {
94+
FLAGS_alsologtostderr = 1;
95+
9496
if (argc != 4) {
9597
printf("This script converts the CIFAR dataset to the leveldb format used\n"
9698
"by caffe to perform classification.\n"

‎examples/cpp_classification/readme.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ script:
4242
The ImageNet labels file (also called the *synset file*) is also
4343
required in order to map a prediction to the name of the class:
4444
```
45-
./data/ilsvrc12/get_ilsvrc_aux.sh.
45+
./data/ilsvrc12/get_ilsvrc_aux.sh
4646
```
4747
Using the files that were downloaded, we can classify the provided cat
4848
image (`examples/images/cat.jpg`) using this command:

‎examples/finetune_flickr_style/readme.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,11 @@ The prototxts in this example assume this, and also assume the presence of the I
5757

5858
We'll also need the ImageNet-trained model, which you can obtain by running `./scripts/download_model_binary.py models/bvlc_reference_caffenet`.
5959

60-
Now we can train! (You can fine-tune in CPU mode by leaving out the `-gpu` flag.)
60+
Now we can train! The key to fine-tuning is the `-weights` argument in the
61+
command below, which tells Caffe that we want to load weights from a pre-trained
62+
Caffe model.
63+
64+
(You can fine-tune in CPU mode by leaving out the `-gpu` flag.)
6165

6266
caffe % ./build/tools/caffe train -solver models/finetune_flickr_style/solver.prototxt -weights models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel -gpu 0
6367

‎examples/mnist/convert_mnist_data.cpp

+16-81
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@
1010
#include <glog/logging.h>
1111
#include <google/protobuf/text_format.h>
1212

13-
#if defined(USE_LEVELDB)
13+
#if defined(USE_LEVELDB) && defined(USE_LMDB)
1414
#include <leveldb/db.h>
1515
#include <leveldb/write_batch.h>
16+
#include <lmdb.h>
1617
#endif
1718

1819
#include <stdint.h>
@@ -21,16 +22,15 @@
2122
#include <fstream> // NOLINT(readability/streams)
2223
#include <string>
2324

24-
#if defined(USE_LMDB)
25-
#include "caffe/util/db_lmdb.hpp"
26-
#endif
27-
25+
#include "boost/scoped_ptr.hpp"
2826
#include "caffe/proto/caffe.pb.h"
27+
#include "caffe/util/db.hpp"
2928
#include "caffe/util/format.hpp"
3029

3130
#if defined(USE_LEVELDB) && defined(USE_LMDB)
3231

3332
using namespace caffe; // NOLINT(build/namespaces)
33+
using boost::scoped_ptr;
3434
using std::string;
3535

3636
DEFINE_string(backend, "lmdb", "The backend for storing the result");
@@ -70,44 +70,10 @@ void convert_dataset(const char* image_filename, const char* label_filename,
7070
image_file.read(reinterpret_cast<char*>(&cols), 4);
7171
cols = swap_endian(cols);
7272

73-
// lmdb
74-
MDB_env *mdb_env;
75-
MDB_dbi mdb_dbi;
76-
MDB_val mdb_key, mdb_data;
77-
MDB_txn *mdb_txn;
78-
// leveldb
79-
leveldb::DB* db;
80-
leveldb::Options options;
81-
options.error_if_exists = true;
82-
options.create_if_missing = true;
83-
options.write_buffer_size = 268435456;
84-
leveldb::WriteBatch* batch = NULL;
85-
86-
// Open db
87-
if (db_backend == "leveldb") { // leveldb
88-
LOG(INFO) << "Opening leveldb " << db_path;
89-
leveldb::Status status = leveldb::DB::Open(
90-
options, db_path, &db);
91-
CHECK(status.ok()) << "Failed to open leveldb " << db_path
92-
<< ". Is it already existing?";
93-
batch = new leveldb::WriteBatch();
94-
} else if (db_backend == "lmdb") { // lmdb
95-
LOG(INFO) << "Opening lmdb " << db_path;
96-
CHECK_EQ(mkdir(db_path, 0744), 0)
97-
<< "mkdir " << db_path << "failed";
98-
CHECK_EQ(mdb_env_create(&mdb_env), MDB_SUCCESS) << "mdb_env_create failed";
99-
CHECK_EQ(mdb_env_set_mapsize(mdb_env, caffe::db::LMDB_MAP_SIZE),
100-
MDB_SUCCESS)
101-
<< "mdb_env_set_mapsize failed";
102-
CHECK_EQ(mdb_env_open(mdb_env, db_path, 0, 0664), MDB_SUCCESS)
103-
<< "mdb_env_open failed";
104-
CHECK_EQ(mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn), MDB_SUCCESS)
105-
<< "mdb_txn_begin failed";
106-
CHECK_EQ(mdb_open(mdb_txn, NULL, 0, &mdb_dbi), MDB_SUCCESS)
107-
<< "mdb_open failed. Does the lmdb already exist? ";
108-
} else {
109-
LOG(FATAL) << "Unknown db backend " << db_backend;
110-
}
73+
74+
scoped_ptr<db::DB> db(db::GetDB(db_backend));
75+
db->Open(db_path, db::NEW);
76+
scoped_ptr<db::Transaction> txn(db->NewTransaction());
11177

11278
// Storing to db
11379
char label;
@@ -129,59 +95,28 @@ void convert_dataset(const char* image_filename, const char* label_filename,
12995
string key_str = caffe::format_int(item_id, 8);
13096
datum.SerializeToString(&value);
13197

132-
// Put in db
133-
if (db_backend == "leveldb") { // leveldb
134-
batch->Put(key_str, value);
135-
} else if (db_backend == "lmdb") { // lmdb
136-
mdb_data.mv_size = value.size();
137-
mdb_data.mv_data = reinterpret_cast<void*>(&value[0]);
138-
mdb_key.mv_size = key_str.size();
139-
mdb_key.mv_data = reinterpret_cast<void*>(&key_str[0]);
140-
CHECK_EQ(mdb_put(mdb_txn, mdb_dbi, &mdb_key, &mdb_data, 0), MDB_SUCCESS)
141-
<< "mdb_put failed";
142-
} else {
143-
LOG(FATAL) << "Unknown db backend " << db_backend;
144-
}
98+
txn->Put(key_str, value);
14599

146100
if (++count % 1000 == 0) {
147-
// Commit txn
148-
if (db_backend == "leveldb") { // leveldb
149-
db->Write(leveldb::WriteOptions(), batch);
150-
delete batch;
151-
batch = new leveldb::WriteBatch();
152-
} else if (db_backend == "lmdb") { // lmdb
153-
CHECK_EQ(mdb_txn_commit(mdb_txn), MDB_SUCCESS)
154-
<< "mdb_txn_commit failed";
155-
CHECK_EQ(mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn), MDB_SUCCESS)
156-
<< "mdb_txn_begin failed";
157-
} else {
158-
LOG(FATAL) << "Unknown db backend " << db_backend;
159-
}
101+
txn->Commit();
160102
}
161103
}
162104
// write the last batch
163105
if (count % 1000 != 0) {
164-
if (db_backend == "leveldb") { // leveldb
165-
db->Write(leveldb::WriteOptions(), batch);
166-
delete batch;
167-
delete db;
168-
} else if (db_backend == "lmdb") { // lmdb
169-
CHECK_EQ(mdb_txn_commit(mdb_txn), MDB_SUCCESS) << "mdb_txn_commit failed";
170-
mdb_close(mdb_env, mdb_dbi);
171-
mdb_env_close(mdb_env);
172-
} else {
173-
LOG(FATAL) << "Unknown db backend " << db_backend;
174-
}
175-
LOG(ERROR) << "Processed " << count << " files.";
106+
txn->Commit();
176107
}
108+
LOG(INFO) << "Processed " << count << " files.";
177109
delete[] pixels;
110+
db->Close();
178111
}
179112

180113
int main(int argc, char** argv) {
181114
#ifndef GFLAGS_GFLAGS_H_
182115
namespace gflags = google;
183116
#endif
184117

118+
FLAGS_alsologtostderr = 1;
119+
185120
gflags::SetUsageMessage("This script converts the MNIST dataset to\n"
186121
"the lmdb/leveldb format used by Caffe to load data.\n"
187122
"Usage:\n"

‎examples/mnist/readme.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ These messages tell you the details about each layer, its connections and its ou
248248
I1203 solver.cpp:36] Solver scaffolding done.
249249
I1203 solver.cpp:44] Solving LeNet
250250

251-
Based on the solver setting, we will print the training loss function every 100 iterations, and test the network every 1000 iterations. You will see messages like this:
251+
Based on the solver setting, we will print the training loss function every 100 iterations, and test the network every 500 iterations. You will see messages like this:
252252

253253
I1203 solver.cpp:204] Iteration 100, lr = 0.00992565
254254
I1203 solver.cpp:66] Iteration 100, loss = 0.26044

‎include/caffe/layers/crop_layer.hpp

+9
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class CropLayer : public Layer<Dtype> {
4444
vector<int> offsets;
4545

4646
private:
47+
// Recursive copy function.
4748
void crop_copy(const vector<Blob<Dtype>*>& bottom,
4849
const vector<Blob<Dtype>*>& top,
4950
const vector<int>& offsets,
@@ -53,6 +54,14 @@ class CropLayer : public Layer<Dtype> {
5354
Dtype* dest_data,
5455
bool is_forward);
5556

57+
// Recursive copy function: this is similar to crop_copy() but loops over all
58+
// but the last two dimensions to allow for ND cropping while still relying on
59+
// a CUDA kernel for the innermost two dimensions for performance reasons. An
60+
// alterantive implementation could rely on the kernel more by passing
61+
// offsets, but this is problematic because of its variable length.
62+
// Since in the standard (N,C,W,H) case N,C are usually not cropped a speedup
63+
// could be achieved by not looping the application of the copy_kernel around
64+
// these dimensions.
5665
void crop_copy_gpu(const vector<Blob<Dtype>*>& bottom,
5766
const vector<Blob<Dtype>*>& top,
5867
const vector<int>& offsets,

‎include/caffe/layers/python_layer.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class PythonLayer : public Layer<Dtype> {
2626
}
2727
self_.attr("param_str") = bp::str(
2828
this->layer_param_.python_param().param_str());
29+
self_.attr("phase") = static_cast<int>(this->phase_);
2930
self_.attr("setup")(bottom, top);
3031
}
3132
virtual void Reshape(const vector<Blob<Dtype>*>& bottom,

‎include/caffe/util/db_lmdb.hpp

+8-5
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <stdint.h>
66
#include <string>
7+
#include <vector>
78

89
#include "lmdb.h"
910

@@ -63,14 +64,16 @@ class LMDBCursor : public Cursor {
6364

6465
class LMDBTransaction : public Transaction {
6566
public:
66-
explicit LMDBTransaction(MDB_dbi* mdb_dbi, MDB_txn* mdb_txn)
67-
: mdb_dbi_(mdb_dbi), mdb_txn_(mdb_txn) { }
67+
explicit LMDBTransaction(MDB_env* mdb_env)
68+
: mdb_env_(mdb_env) { }
6869
virtual void Put(const string& key, const string& value);
69-
virtual void Commit() { MDB_CHECK(mdb_txn_commit(mdb_txn_)); }
70+
virtual void Commit();
7071

7172
private:
72-
MDB_dbi* mdb_dbi_;
73-
MDB_txn* mdb_txn_;
73+
MDB_env* mdb_env_;
74+
vector<string> keys, values;
75+
76+
void DoubleMapSize();
7477

7578
DISABLE_COPY_AND_ASSIGN(LMDBTransaction);
7679
};

‎python/caffe/_caffe.cpp

+17-4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,19 @@
3030
#define STRINGIZE(m) #m
3131
#define STRINGIZE2(m) STRINGIZE(m)
3232

33+
/* Fix to avoid registration warnings in pycaffe (#3960) */
34+
#define BP_REGISTER_SHARED_PTR_TO_PYTHON(PTR) do { \
35+
const boost::python::type_info info = \
36+
boost::python::type_id<shared_ptr<PTR > >(); \
37+
const boost::python::converter::registration* reg = \
38+
boost::python::converter::registry::query(info); \
39+
if (reg == NULL) { \
40+
bp::register_ptr_to_python<shared_ptr<PTR > >(); \
41+
} else if ((*reg).m_to_python == NULL) { \
42+
bp::register_ptr_to_python<shared_ptr<PTR > >(); \
43+
} \
44+
} while (0)
45+
3346
namespace bp = boost::python;
3447

3548
namespace caffe {
@@ -259,7 +272,7 @@ BOOST_PYTHON_MODULE(_caffe) {
259272
.def("_set_input_arrays", &Net_SetInputArrays,
260273
bp::with_custodian_and_ward<1, 2, bp::with_custodian_and_ward<1, 3> >())
261274
.def("save", &Net_Save);
262-
bp::register_ptr_to_python<shared_ptr<Net<Dtype> > >();
275+
BP_REGISTER_SHARED_PTR_TO_PYTHON(Net<Dtype>);
263276

264277
bp::class_<Blob<Dtype>, shared_ptr<Blob<Dtype> >, boost::noncopyable>(
265278
"Blob", bp::no_init)
@@ -279,7 +292,7 @@ BOOST_PYTHON_MODULE(_caffe) {
279292
NdarrayCallPolicies()))
280293
.add_property("diff", bp::make_function(&Blob<Dtype>::mutable_cpu_diff,
281294
NdarrayCallPolicies()));
282-
bp::register_ptr_to_python<shared_ptr<Blob<Dtype> > >();
295+
BP_REGISTER_SHARED_PTR_TO_PYTHON(Blob<Dtype>);
283296

284297
bp::class_<Layer<Dtype>, shared_ptr<PythonLayer<Dtype> >,
285298
boost::noncopyable>("Layer", bp::init<const LayerParameter&>())
@@ -288,7 +301,7 @@ BOOST_PYTHON_MODULE(_caffe) {
288301
.def("setup", &Layer<Dtype>::LayerSetUp)
289302
.def("reshape", &Layer<Dtype>::Reshape)
290303
.add_property("type", bp::make_function(&Layer<Dtype>::type));
291-
bp::register_ptr_to_python<shared_ptr<Layer<Dtype> > >();
304+
BP_REGISTER_SHARED_PTR_TO_PYTHON(Layer<Dtype>);
292305

293306
bp::class_<LayerParameter>("LayerParameter", bp::no_init);
294307

@@ -303,7 +316,7 @@ BOOST_PYTHON_MODULE(_caffe) {
303316
.def("step", &Solver<Dtype>::Step)
304317
.def("restore", &Solver<Dtype>::Restore)
305318
.def("snapshot", &Solver<Dtype>::Snapshot);
306-
bp::register_ptr_to_python<shared_ptr<Solver<Dtype> > >();
319+
BP_REGISTER_SHARED_PTR_TO_PYTHON(Solver<Dtype>);
307320

308321
bp::class_<SGDSolver<Dtype>, bp::bases<Solver<Dtype> >,
309322
shared_ptr<SGDSolver<Dtype> >, boost::noncopyable>(

‎python/caffe/classifier.py

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ def predict(self, inputs, oversample=True):
7979
-self.crop_dims / 2.0,
8080
self.crop_dims / 2.0
8181
])
82+
crop = crop.astype(int)
8283
input_ = input_[:, crop[0]:crop[2], crop[1]:crop[3], :]
8384

8485
# Classify

‎python/caffe/draw.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def get_pydot_graph(caffe_net, rankdir, label_edges=True):
142142
-------
143143
pydot graph object
144144
"""
145-
pydot_graph = pydot.Dot(caffe_net.name,
145+
pydot_graph = pydot.Dot(caffe_net.name if caffe_net.name else 'Net',
146146
graph_type='digraph',
147147
rankdir=rankdir)
148148
pydot_nodes = {}

‎python/caffe/io.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def blobprotovector_str_to_arraylist(str):
6363
return [blobproto_to_array(blob) for blob in vec.blobs]
6464

6565

66-
def array_to_datum(arr, label=0):
66+
def array_to_datum(arr, label=None):
6767
"""Converts a 3-dimensional array to datum. If the array has dtype uint8,
6868
the output data will be encoded as a string. Otherwise, the output data
6969
will be stored in float format.
@@ -76,7 +76,8 @@ def array_to_datum(arr, label=0):
7676
datum.data = arr.tostring()
7777
else:
7878
datum.float_data.extend(arr.flat)
79-
datum.label = label
79+
if label is not None:
80+
datum.label = label
8081
return datum
8182

8283

‎python/caffe/net_spec.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def param_name_dict():
3232
# get all parameter names (typically underscore case) and corresponding
3333
# type names (typically camel case), which contain the layer names
3434
# (note that not all parameters correspond to layers, but we'll ignore that)
35-
param_names = [s for s in dir(layer) if s.endswith('_param')]
35+
param_names = [f.name for f in layer.DESCRIPTOR.fields if f.name.endswith('_param')]
3636
param_type_names = [type(getattr(layer, s)).__name__ for s in param_names]
3737
# strip the final '_param' or 'Parameter'
3838
param_names = [s[:-len('_param')] for s in param_names]

‎python/caffe/pycaffe.py

+21-7
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ def _Net_blobs(self):
2727
An OrderedDict (bottom to top, i.e., input to output) of network
2828
blobs indexed by name
2929
"""
30-
return OrderedDict(zip(self._blob_names, self._blobs))
30+
if not hasattr(self, '_blobs_dict'):
31+
self._blobs_dict = OrderedDict(zip(self._blob_names, self._blobs))
32+
return self._blobs_dict
3133

3234

3335
@property
@@ -36,7 +38,10 @@ def _Net_blob_loss_weights(self):
3638
An OrderedDict (bottom to top, i.e., input to output) of network
3739
blob loss weights indexed by name
3840
"""
39-
return OrderedDict(zip(self._blob_names, self._blob_loss_weights))
41+
if not hasattr(self, '_blobs_loss_weights_dict'):
42+
self._blob_loss_weights_dict = OrderedDict(zip(self._blob_names,
43+
self._blob_loss_weights))
44+
return self._blob_loss_weights_dict
4045

4146

4247
@property
@@ -46,19 +51,28 @@ def _Net_params(self):
4651
parameters indexed by name; each is a list of multiple blobs (e.g.,
4752
weights and biases)
4853
"""
49-
return OrderedDict([(name, lr.blobs)
50-
for name, lr in zip(self._layer_names, self.layers)
51-
if len(lr.blobs) > 0])
54+
if not hasattr(self, '_params_dict'):
55+
self._params_dict = OrderedDict([(name, lr.blobs)
56+
for name, lr in zip(
57+
self._layer_names, self.layers)
58+
if len(lr.blobs) > 0])
59+
return self._params_dict
5260

5361

5462
@property
5563
def _Net_inputs(self):
56-
return [list(self.blobs.keys())[i] for i in self._inputs]
64+
if not hasattr(self, '_input_list'):
65+
keys = list(self.blobs.keys())
66+
self._input_list = [keys[i] for i in self._inputs]
67+
return self._input_list
5768

5869

5970
@property
6071
def _Net_outputs(self):
61-
return [list(self.blobs.keys())[i] for i in self._outputs]
72+
if not hasattr(self, '_output_list'):
73+
keys = list(self.blobs.keys())
74+
self._output_list = [keys[i] for i in self._outputs]
75+
return self._output_list
6276

6377

6478
def _Net_forward(self, blobs=None, start=None, end=None, **kwargs):

‎python/caffe/test/test_io.py

+15
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,18 @@ def test_scalar(self):
3939

4040
arr = caffe.io.blobproto_to_array(blob)
4141
self.assertEqual(arr, 123)
42+
43+
44+
class TestArrayToDatum(unittest.TestCase):
45+
46+
def test_label_none_size(self):
47+
# Set label
48+
d1 = caffe.io.array_to_datum(
49+
np.ones((10,10,3)), label=1)
50+
# Don't set label
51+
d2 = caffe.io.array_to_datum(
52+
np.ones((10,10,3)))
53+
# Not setting the label should result in a smaller object
54+
self.assertGreater(
55+
len(d1.SerializeToString()),
56+
len(d2.SerializeToString()))

‎python/caffe/test/test_python_layer.py

+26
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ def forward(self, bottom, top):
4444
def backward(self, top, propagate_down, bottom):
4545
self.blobs[0].diff[0] = 1
4646

47+
class PhaseLayer(caffe.Layer):
48+
"""A layer for checking attribute `phase`"""
49+
50+
def setup(self, bottom, top):
51+
pass
52+
53+
def reshape(self, bootom, top):
54+
top[0].reshape()
55+
56+
def forward(self, bottom, top):
57+
top[0].data[()] = self.phase
58+
4759
def python_net_file():
4860
with tempfile.NamedTemporaryFile(mode='w+', delete=False) as f:
4961
f.write("""name: 'pythonnet' force_backward: true
@@ -76,6 +88,14 @@ def parameter_net_file():
7688
""")
7789
return f.name
7890

91+
def phase_net_file():
92+
with tempfile.NamedTemporaryFile(mode='w+', delete=False) as f:
93+
f.write("""name: 'pythonnet' force_backward: true
94+
layer { type: 'Python' name: 'layer' top: 'phase'
95+
python_param { module: 'test_python_layer' layer: 'PhaseLayer' } }
96+
""")
97+
return f.name
98+
7999

80100
@unittest.skipIf('Python' not in caffe.layer_type_list(),
81101
'Caffe built without Python layer support')
@@ -140,3 +160,9 @@ def test_parameter(self):
140160
self.assertEqual(layer.blobs[0].data[0], 1)
141161

142162
os.remove(net_file)
163+
164+
def test_phase(self):
165+
net_file = phase_net_file()
166+
for phase in caffe.TRAIN, caffe.TEST:
167+
net = caffe.Net(net_file, phase)
168+
self.assertEqual(net.forward()['phase'], phase)

‎scripts/download_model_binary.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def valid_dirname(dirname):
6060

6161
# Closure-d function for checking SHA1.
6262
def model_checks_out(filename=model_filename, sha1=frontmatter['sha1']):
63-
with open(filename, 'r') as f:
63+
with open(filename, 'rb') as f:
6464
return hashlib.sha1(f.read()).hexdigest() == sha1
6565

6666
# Check if model exists.

‎src/caffe/layers/crop_layer.cpp

+11-18
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ namespace caffe {
1515
template <typename Dtype>
1616
void CropLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
1717
const vector<Blob<Dtype>*>& top) {
18-
// All logic that depends only on the number of dimensions is here,
19-
// the rest is in Reshape because it depends on Blob size.
18+
// LayerSetup() handles the number of dimensions; Reshape() handles the sizes.
2019
// bottom[0] supplies the data
2120
// bottom[1] supplies the size
2221
const CropParameter& param = this->layer_param_.crop_param();
@@ -40,41 +39,35 @@ void CropLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
4039
int input_dim = bottom[0]->num_axes();
4140
const int start_axis = bottom[0]->CanonicalAxisIndex(param.axis());
4241

43-
// initialize all offsets to 0
42+
// Initialize offsets to 0 and the new shape to the current shape of the data.
4443
offsets = vector<int>(input_dim, 0);
45-
// initialize new shape to bottom[0]
4644
vector<int> new_shape(bottom[0]->shape());
4745

48-
// apply crops
46+
// Determine crop offsets and the new shape post-crop.
4947
for (int i = 0; i < input_dim; ++i) {
5048
int crop_offset = 0;
51-
int new_size = bottom[0]->shape(i);
49+
int new_size = bottom[0]->shape(i);
5250
if (i >= start_axis) {
5351
new_size = bottom[1]->shape(i);
54-
5552
if (param.offset_size() == 1) {
56-
// if only one crop value is supplied, crop all dimensions after axis
57-
// by this crop value
53+
// If only one offset is given, all crops have the same offset.
5854
crop_offset = param.offset(0);
5955
} else if (param.offset_size() > 1) {
60-
// crop values specified must be equal to the number of dimensions
61-
// following axis
56+
// For several offsets, the number of offsets must be equal to the
57+
// number of dimensions to crop, that is dimensions after the axis.
6258
crop_offset = param.offset(i - start_axis);
6359
}
60+
// Check that the crop and offset are within the dimension's bounds.
61+
CHECK_GE(bottom[0]->shape(i) - crop_offset, bottom[1]->shape(i))
62+
<< "the crop for dimension " << i << " is out-of-bounds with "
63+
<< "size " << bottom[1]->shape(i) << " and offset " << crop_offset;
6464
}
65-
// Check that the image we are cropping minus the margin is bigger
66-
// than the destination image.
67-
CHECK_GE(bottom[0]->shape(i) - crop_offset,
68-
bottom[1]->shape(i))
69-
<< "invalid crop parameters in dimension: " << i;
70-
// Now set new size and offsets
7165
new_shape[i] = new_size;
7266
offsets[i] = crop_offset;
7367
}
7468
top[0]->Reshape(new_shape);
7569
}
7670

77-
// recursive copy function
7871
template <typename Dtype>
7972
void CropLayer<Dtype>::crop_copy(const vector<Blob<Dtype>*>& bottom,
8073
const vector<Blob<Dtype>*>& top,

‎src/caffe/layers/crop_layer.cu

-9
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,6 @@ __global__ void copy_kernel(const int n, const int height, const int width,
2222
}
2323
}
2424

25-
// recursive copy function, this function is similar to crop_copy but loops
26-
// over all but the last two dimensions. It is implemented this way to allow
27-
// for ND cropping while still relying on a CUDA kernel for the innermost
28-
// two dimensions for performance reasons.
29-
// An alternative way to implement ND cropping relying more on the kernel
30-
// would require passing offsets to the kernel, which is a bit problematic
31-
// because it is of variable length. Since in the standard (N,C,W,H) case
32-
// N,C are usually not cropped a speedup could be achieved by not looping
33-
// the application of the copy_kernel around these dimensions.
3425
template <typename Dtype>
3526
void CropLayer<Dtype>::crop_copy_gpu(const vector<Blob<Dtype>*>& bottom,
3627
const vector<Blob<Dtype>*>& top,

‎src/caffe/layers/exp_layer.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ void ExpLayer<Dtype>::LayerSetUp(const vector<Blob<Dtype>*>& bottom,
2323
const Dtype input_scale = this->layer_param_.exp_param().scale();
2424
const Dtype input_shift = this->layer_param_.exp_param().shift();
2525
inner_scale_ = log_base * input_scale;
26-
outer_scale_ = (input_shift == Dtype(0)) ? Dtype(1) : pow(base, input_shift);
26+
outer_scale_ = (input_shift == Dtype(0)) ? Dtype(1) :
27+
( (base != Dtype(-1)) ? pow(base, input_shift) : exp(input_shift) );
2728
}
2829

2930
template <typename Dtype>

‎src/caffe/net.cpp

+4-5
Original file line numberDiff line numberDiff line change
@@ -430,12 +430,11 @@ int Net<Dtype>::AppendBottom(const NetParameter& param, const int layer_id,
430430
bottom_vecs_[layer_id].push_back(blobs_[blob_id].get());
431431
bottom_id_vecs_[layer_id].push_back(blob_id);
432432
available_blobs->erase(blob_name);
433-
bool propagate_down = true;
433+
bool need_backward = blob_need_backward_[blob_id];
434434
// Check if the backpropagation on bottom_id should be skipped
435-
if (layer_param.propagate_down_size() > 0)
436-
propagate_down = layer_param.propagate_down(bottom_id);
437-
const bool need_backward = blob_need_backward_[blob_id] &&
438-
propagate_down;
435+
if (layer_param.propagate_down_size() > 0) {
436+
need_backward = layer_param.propagate_down(bottom_id);
437+
}
439438
bottom_need_backward_[layer_id].push_back(need_backward);
440439
return blob_id;
441440
}

‎src/caffe/proto/caffe.proto

+7-2
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,12 @@ message LayerParameter {
328328
// The blobs containing the numeric parameters of the layer.
329329
repeated BlobProto blobs = 7;
330330

331-
// Specifies on which bottoms the backpropagation should be skipped.
331+
// Specifies whether to backpropagate to each bottom. If unspecified,
332+
// Caffe will automatically infer whether each input needs backpropagation
333+
// to compute parameter gradients. If set to true for some inputs,
334+
// backpropagation to those inputs is forced; if set false for some inputs,
335+
// backpropagation to those inputs is skipped.
336+
//
332337
// The size must be either 0 or equal to the number of bottoms.
333338
repeated bool propagate_down = 11;
334339

@@ -990,7 +995,7 @@ message ReshapeParameter {
990995
// reshape_param { shape { dim: 2 dim: 2 dim: 4 } }
991996
// reshape_param { shape { dim: 0 dim: 2 dim: 4 } }
992997
// reshape_param { shape { dim: 0 dim: 2 dim: -1 } }
993-
// reshape_param { shape { dim: -1 dim: 0 dim: 2 } }
998+
// reshape_param { shape { dim: 0 dim:-1 dim: 4 } }
994999
//
9951000
optional BlobShape shape = 1;
9961001

‎src/caffe/test/test_crop_layer.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,24 @@ TYPED_TEST(CropLayerTest, TestSetupShapeNegativeIndexing) {
9191
}
9292
}
9393

94+
TYPED_TEST(CropLayerTest, TestDimensionsCheck) {
95+
typedef typename TypeParam::Dtype Dtype;
96+
LayerParameter layer_param;
97+
// Reshape size blob to have incompatible sizes for uncropped dimensions:
98+
// the size blob has more channels than the data blob, but this is fine
99+
// since the channels dimension is not cropped in this configuration.
100+
this->blob_bottom_1_->Reshape(2, 5, 4, 2);
101+
CropLayer<Dtype> layer(layer_param);
102+
layer.SetUp(this->blob_bottom_vec_, this->blob_top_vec_);
103+
for (int i = 0; i < this->blob_top_->num_axes(); ++i) {
104+
if (i < 2) {
105+
EXPECT_EQ(this->blob_bottom_0_->shape(i), this->blob_top_->shape(i));
106+
} else {
107+
EXPECT_EQ(this->blob_bottom_1_->shape(i), this->blob_top_->shape(i));
108+
}
109+
}
110+
}
111+
94112
TYPED_TEST(CropLayerTest, TestCropAll) {
95113
typedef typename TypeParam::Dtype Dtype;
96114
LayerParameter layer_param;

‎src/caffe/test/test_net.cpp

+102
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,61 @@ class NetTest : public MultiDeviceTest<TypeParam> {
716716
InitNetFromProtoString(proto);
717717
}
718718

719+
virtual void InitForcePropNet(bool test_force_true) {
720+
string proto =
721+
"name: 'ForcePropTestNetwork' "
722+
"layer { "
723+
" name: 'data' "
724+
" type: 'DummyData' "
725+
" dummy_data_param { "
726+
" shape { "
727+
" dim: 5 "
728+
" dim: 2 "
729+
" dim: 3 "
730+
" dim: 4 "
731+
" } "
732+
" data_filler { "
733+
" type: 'gaussian' "
734+
" std: 0.01 "
735+
" } "
736+
" shape { "
737+
" dim: 5 "
738+
" } "
739+
" data_filler { "
740+
" type: 'constant' "
741+
" value: 0 "
742+
" } "
743+
" } "
744+
" top: 'data' "
745+
" top: 'label' "
746+
"} "
747+
"layer { "
748+
" name: 'innerproduct' "
749+
" type: 'InnerProduct' "
750+
" inner_product_param { "
751+
" num_output: 1 "
752+
" weight_filler { "
753+
" type: 'gaussian' "
754+
" std: 0.01 "
755+
" } "
756+
" } "
757+
" bottom: 'data' "
758+
" top: 'innerproduct' ";
759+
if (test_force_true) {
760+
proto += " propagate_down: true ";
761+
}
762+
proto +=
763+
"} "
764+
"layer { "
765+
" name: 'loss' "
766+
" bottom: 'innerproduct' "
767+
" bottom: 'label' "
768+
" top: 'cross_entropy_loss' "
769+
" type: 'SigmoidCrossEntropyLoss' "
770+
"} ";
771+
InitNetFromProtoString(proto);
772+
}
773+
719774
int seed_;
720775
shared_ptr<Net<Dtype> > net_;
721776
};
@@ -2371,4 +2426,51 @@ TYPED_TEST(NetTest, TestSkipPropagateDown) {
23712426
}
23722427
}
23732428

2429+
TYPED_TEST(NetTest, TestForcePropagateDown) {
2430+
this->InitForcePropNet(false);
2431+
vector<bool> layer_need_backward = this->net_->layer_need_backward();
2432+
for (int layer_id = 0; layer_id < this->net_->layers().size(); ++layer_id) {
2433+
const string& layer_name = this->net_->layer_names()[layer_id];
2434+
const vector<bool> need_backward =
2435+
this->net_->bottom_need_backward()[layer_id];
2436+
if (layer_name == "data") {
2437+
ASSERT_EQ(need_backward.size(), 0);
2438+
EXPECT_FALSE(layer_need_backward[layer_id]);
2439+
} else if (layer_name == "innerproduct") {
2440+
ASSERT_EQ(need_backward.size(), 1);
2441+
EXPECT_FALSE(need_backward[0]); // data
2442+
EXPECT_TRUE(layer_need_backward[layer_id]);
2443+
} else if (layer_name == "loss") {
2444+
ASSERT_EQ(need_backward.size(), 2);
2445+
EXPECT_TRUE(need_backward[0]); // innerproduct
2446+
EXPECT_FALSE(need_backward[1]); // label
2447+
EXPECT_TRUE(layer_need_backward[layer_id]);
2448+
} else {
2449+
LOG(FATAL) << "Unknown layer: " << layer_name;
2450+
}
2451+
}
2452+
this->InitForcePropNet(true);
2453+
layer_need_backward = this->net_->layer_need_backward();
2454+
for (int layer_id = 0; layer_id < this->net_->layers().size(); ++layer_id) {
2455+
const string& layer_name = this->net_->layer_names()[layer_id];
2456+
const vector<bool> need_backward =
2457+
this->net_->bottom_need_backward()[layer_id];
2458+
if (layer_name == "data") {
2459+
ASSERT_EQ(need_backward.size(), 0);
2460+
EXPECT_FALSE(layer_need_backward[layer_id]);
2461+
} else if (layer_name == "innerproduct") {
2462+
ASSERT_EQ(need_backward.size(), 1);
2463+
EXPECT_TRUE(need_backward[0]); // data
2464+
EXPECT_TRUE(layer_need_backward[layer_id]);
2465+
} else if (layer_name == "loss") {
2466+
ASSERT_EQ(need_backward.size(), 2);
2467+
EXPECT_TRUE(need_backward[0]); // innerproduct
2468+
EXPECT_FALSE(need_backward[1]); // label
2469+
EXPECT_TRUE(layer_need_backward[layer_id]);
2470+
} else {
2471+
LOG(FATAL) << "Unknown layer: " << layer_name;
2472+
}
2473+
}
2474+
}
2475+
23742476
} // namespace caffe

‎src/caffe/test/test_neuron_layer.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,26 @@ TYPED_TEST(NeuronLayerTest, TestExpGradient) {
394394
this->TestExpGradient(kBase, kScale, kShift);
395395
}
396396

397+
TYPED_TEST(NeuronLayerTest, TestExpLayerWithShift) {
398+
typedef typename TypeParam::Dtype Dtype;
399+
// Test default base of "-1" -- should actually set base := e,
400+
// with a non-zero shift
401+
const Dtype kBase = -1;
402+
const Dtype kScale = 1;
403+
const Dtype kShift = 1;
404+
this->TestExpForward(kBase, kScale, kShift);
405+
}
406+
407+
TYPED_TEST(NeuronLayerTest, TestExpGradientWithShift) {
408+
typedef typename TypeParam::Dtype Dtype;
409+
// Test default base of "-1" -- should actually set base := e,
410+
// with a non-zero shift
411+
const Dtype kBase = -1;
412+
const Dtype kScale = 1;
413+
const Dtype kShift = 1;
414+
this->TestExpGradient(kBase, kScale, kShift);
415+
}
416+
397417
TYPED_TEST(NeuronLayerTest, TestExpLayerBase2) {
398418
typedef typename TypeParam::Dtype Dtype;
399419
const Dtype kBase = 2;

‎src/caffe/util/db_lmdb.cpp

+52-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ namespace caffe { namespace db {
99

1010
void LMDB::Open(const string& source, Mode mode) {
1111
MDB_CHECK(mdb_env_create(&mdb_env_));
12-
MDB_CHECK(mdb_env_set_mapsize(mdb_env_, LMDB_MAP_SIZE));
1312
if (mode == NEW) {
1413
CHECK_EQ(mkdir(source.c_str(), 0744), 0) << "mkdir " << source << "failed";
1514
}
@@ -46,19 +45,61 @@ LMDBCursor* LMDB::NewCursor() {
4645
}
4746

4847
LMDBTransaction* LMDB::NewTransaction() {
49-
MDB_txn* mdb_txn;
50-
MDB_CHECK(mdb_txn_begin(mdb_env_, NULL, 0, &mdb_txn));
51-
MDB_CHECK(mdb_dbi_open(mdb_txn, NULL, 0, &mdb_dbi_));
52-
return new LMDBTransaction(&mdb_dbi_, mdb_txn);
48+
return new LMDBTransaction(mdb_env_);
5349
}
5450

5551
void LMDBTransaction::Put(const string& key, const string& value) {
56-
MDB_val mdb_key, mdb_value;
57-
mdb_key.mv_data = const_cast<char*>(key.data());
58-
mdb_key.mv_size = key.size();
59-
mdb_value.mv_data = const_cast<char*>(value.data());
60-
mdb_value.mv_size = value.size();
61-
MDB_CHECK(mdb_put(mdb_txn_, *mdb_dbi_, &mdb_key, &mdb_value, 0));
52+
keys.push_back(key);
53+
values.push_back(value);
54+
}
55+
56+
void LMDBTransaction::Commit() {
57+
MDB_dbi mdb_dbi;
58+
MDB_val mdb_key, mdb_data;
59+
MDB_txn *mdb_txn;
60+
61+
// Initialize MDB variables
62+
MDB_CHECK(mdb_txn_begin(mdb_env_, NULL, 0, &mdb_txn));
63+
MDB_CHECK(mdb_dbi_open(mdb_txn, NULL, 0, &mdb_dbi));
64+
65+
bool out_of_memory = false;
66+
for (int i = 0; i < keys.size(); i++) {
67+
mdb_key.mv_size = keys[i].size();
68+
mdb_key.mv_data = const_cast<char*>(keys[i].data());
69+
mdb_data.mv_size = values[i].size();
70+
mdb_data.mv_data = const_cast<char*>(values[i].data());
71+
72+
int put_rc = mdb_put(mdb_txn, mdb_dbi, &mdb_key, &mdb_data, 0);
73+
if (put_rc == MDB_MAP_FULL) {
74+
out_of_memory = true;
75+
break;
76+
} else {
77+
// Failed for some other reason
78+
MDB_CHECK(put_rc);
79+
}
80+
}
81+
82+
if (!out_of_memory) {
83+
// Commit the transaction
84+
MDB_CHECK(mdb_txn_commit(mdb_txn));
85+
mdb_dbi_close(mdb_env_, mdb_dbi);
86+
keys.clear();
87+
values.clear();
88+
} else {
89+
// Double the map size and retry
90+
mdb_txn_abort(mdb_txn);
91+
mdb_dbi_close(mdb_env_, mdb_dbi);
92+
DoubleMapSize();
93+
Commit();
94+
}
95+
}
96+
97+
void LMDBTransaction::DoubleMapSize() {
98+
struct MDB_envinfo current_info;
99+
MDB_CHECK(mdb_env_info(mdb_env_, &current_info));
100+
size_t new_size = current_info.me_mapsize * 2;
101+
DLOG(INFO) << "Doubling LMDB map size to " << (new_size>>20) << "MB ...";
102+
MDB_CHECK(mdb_env_set_mapsize(mdb_env_, new_size));
62103
}
63104

64105
} // namespace db

‎tools/caffe.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ DEFINE_string(gpu, "",
3535
DEFINE_string(solver, "",
3636
"The solver definition protocol buffer text file.");
3737
DEFINE_string(model, "",
38-
"The model definition protocol buffer text file..");
38+
"The model definition protocol buffer text file.");
3939
DEFINE_string(snapshot, "",
4040
"Optional; the snapshot solver state to resume training.");
4141
DEFINE_string(weights, "",

‎tools/extra/plot_training_log.py.example

+15-11
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import matplotlib.legend as lgd
1010
import matplotlib.markers as mks
1111

1212
def get_log_parsing_script():
13-
dirname = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
13+
dirname = os.path.dirname(os.path.abspath(inspect.getfile(
14+
inspect.currentframe())))
1415
return dirname + '/parse_log.sh'
1516

1617
def get_log_file_suffix():
@@ -61,16 +62,17 @@ def get_data_file_type(chart_type):
6162
return data_file_type
6263

6364
def get_data_file(chart_type, path_to_log):
64-
return os.path.basename(path_to_log) + '.' + get_data_file_type(chart_type).lower()
65+
return (os.path.basename(path_to_log) + '.' +
66+
get_data_file_type(chart_type).lower())
6567

6668
def get_field_descriptions(chart_type):
6769
description = get_chart_type_description(chart_type).split(
6870
get_chart_type_description_separator())
6971
y_axis_field = description[0]
7072
x_axis_field = description[1]
71-
return x_axis_field, y_axis_field
73+
return x_axis_field, y_axis_field
7274

73-
def get_field_indecies(x_axis_field, y_axis_field):
75+
def get_field_indices(x_axis_field, y_axis_field):
7476
data_file_type = get_data_file_type(chart_type)
7577
fields = create_field_index()[0][data_file_type]
7678
return fields[x_axis_field], fields[y_axis_field]
@@ -111,7 +113,7 @@ def plot_chart(chart_type, path_to_png, path_to_log_list):
111113
os.system('%s %s' % (get_log_parsing_script(), path_to_log))
112114
data_file = get_data_file(chart_type, path_to_log)
113115
x_axis_field, y_axis_field = get_field_descriptions(chart_type)
114-
x, y = get_field_indecies(x_axis_field, y_axis_field)
116+
x, y = get_field_indices(x_axis_field, y_axis_field)
115117
data = load_data(data_file, x, y)
116118
## TODO: more systematic color cycle for lines
117119
color = [random.random(), random.random(), random.random()]
@@ -138,8 +140,8 @@ def plot_chart(chart_type, path_to_png, path_to_log_list):
138140
plt.legend(loc = legend_loc, ncol = 1) # ajust ncol to fit the space
139141
plt.title(get_chart_type_description(chart_type))
140142
plt.xlabel(x_axis_field)
141-
plt.ylabel(y_axis_field)
142-
plt.savefig(path_to_png)
143+
plt.ylabel(y_axis_field)
144+
plt.savefig(path_to_png)
143145
plt.show()
144146

145147
def print_help():
@@ -160,28 +162,30 @@ Supported chart types:""" % (len(get_supported_chart_types()) - 1,
160162
num = len(supported_chart_types)
161163
for i in xrange(num):
162164
print ' %d: %s' % (i, supported_chart_types[i])
163-
exit
165+
sys.exit()
164166

165167
def is_valid_chart_type(chart_type):
166168
return chart_type >= 0 and chart_type < len(get_supported_chart_types())
167-
169+
168170
if __name__ == '__main__':
169171
if len(sys.argv) < 4:
170172
print_help()
171173
else:
172174
chart_type = int(sys.argv[1])
173175
if not is_valid_chart_type(chart_type):
176+
print '%s is not a valid chart type.' % chart_type
174177
print_help()
175178
path_to_png = sys.argv[2]
176179
if not path_to_png.endswith('.png'):
177180
print 'Path must ends with png' % path_to_png
178-
exit
181+
sys.exit()
179182
path_to_logs = sys.argv[3:]
180183
for path_to_log in path_to_logs:
181184
if not os.path.exists(path_to_log):
182185
print 'Path does not exist: %s' % path_to_log
183-
exit
186+
sys.exit()
184187
if not path_to_log.endswith(get_log_file_suffix()):
188+
print 'Log file must end in %s.' % get_log_file_suffix()
185189
print_help()
186190
## plot_chart accpets multiple path_to_logs
187191
plot_chart(chart_type, path_to_png, path_to_logs)

0 commit comments

Comments
 (0)
Please sign in to comment.