diff --git a/.travis.yml b/.travis.yml index 3deb45a2f0c..955aa8c3ba2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,8 @@ install: - sudo -E $SCRIPTS/travis_install.sh before_script: - - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib + - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:/usr/local/cuda/lib64 + - export PATH=/home/travis/miniconda/bin:$PATH - if ! $WITH_CMAKE; then $SCRIPTS/travis_setup_makefile_config.sh; fi script: $SCRIPTS/travis_build_and_test.sh diff --git a/Makefile b/Makefile index 46bf7280919..d3bddea7bea 100644 --- a/Makefile +++ b/Makefile @@ -434,6 +434,9 @@ runtest: $(TEST_ALL_BIN) $(TEST_ALL_DYNLINK_BIN) $(TEST_ALL_BIN) $(TEST_GPUID) --gtest_shuffle $(TEST_FILTER) && \ $(TEST_ALL_DYNLINK_BIN) $(TEST_GPUID) --gtest_shuffle $(TEST_FILTER) +pytest: py + cd python; python -m unittest discover -s caffe/test + warn: $(EMPTY_WARN_REPORT) $(EMPTY_WARN_REPORT): $(ALL_WARNS) | $(BUILD_DIR) diff --git a/python/caffe/test/test_net.py b/python/caffe/test/test_net.py new file mode 100644 index 00000000000..f0e9deef19c --- /dev/null +++ b/python/caffe/test/test_net.py @@ -0,0 +1,77 @@ +import unittest +import tempfile +import os +import numpy as np + +import caffe + +def simple_net_file(num_output): + """Make a simple net prototxt, based on test_net.cpp, returning the name + of the (temporary) file.""" + + f = tempfile.NamedTemporaryFile(delete=False) + f.write("""name: 'testnet' force_backward: true + layers { type: DUMMY_DATA name: 'data' top: 'data' top: 'label' + dummy_data_param { num: 5 channels: 2 height: 3 width: 4 + num: 5 channels: 1 height: 1 width: 1 + data_filler { type: 'gaussian' std: 1 } + data_filler { type: 'constant' } } } + layers { type: CONVOLUTION name: 'conv' bottom: 'data' top: 'conv' + convolution_param { num_output: 11 kernel_size: 2 pad: 3 + weight_filler { type: 'gaussian' std: 1 } + bias_filler { type: 'constant' value: 2 } } + weight_decay: 1 weight_decay: 0 } + layers { type: INNER_PRODUCT name: 'ip' bottom: 'conv' top: 'ip' + inner_product_param { num_output: """ + str(num_output) + """ + weight_filler { type: 'gaussian' std: 2.5 } + bias_filler { type: 'constant' value: -3 } } } + layers { type: SOFTMAX_LOSS name: 'loss' bottom: 'ip' bottom: 'label' + top: 'loss' }""") + f.close() + return f.name + +class TestNet(unittest.TestCase): + def setUp(self): + self.num_output = 13 + net_file = simple_net_file(self.num_output) + self.net = caffe.Net(net_file) + # fill in valid labels + self.net.blobs['label'].data[...] = \ + np.random.randint(self.num_output, + size=self.net.blobs['label'].data.shape) + os.remove(net_file) + + def test_memory(self): + """Check that holding onto blob data beyond the life of a Net is OK""" + + params = sum(map(list, self.net.params.itervalues()), []) + blobs = self.net.blobs.values() + del self.net + + # now sum everything (forcing all memory to be read) + total = 0 + for p in params: + total += p.data.sum() + p.diff.sum() + for bl in blobs: + total += bl.data.sum() + bl.diff.sum() + + def test_forward_backward(self): + self.net.forward() + self.net.backward() + + def test_inputs_outputs(self): + self.assertEqual(self.net.inputs, []) + self.assertEqual(self.net.outputs, ['loss']) + + def test_save_and_read(self): + f = tempfile.NamedTemporaryFile(delete=False) + f.close() + self.net.save(f.name) + net_file = simple_net_file(self.num_output) + net2 = caffe.Net(net_file, f.name) + os.remove(net_file) + os.remove(f.name) + for name in self.net.params: + for i in range(len(self.net.params[name])): + self.assertEqual(abs(self.net.params[name][i].data + - net2.params[name][i].data).sum(), 0) diff --git a/python/caffe/test/test_solver.py b/python/caffe/test/test_solver.py new file mode 100644 index 00000000000..b78c91f9978 --- /dev/null +++ b/python/caffe/test/test_solver.py @@ -0,0 +1,49 @@ +import unittest +import tempfile +import os +import numpy as np + +import caffe +from test_net import simple_net_file + +class TestSolver(unittest.TestCase): + def setUp(self): + self.num_output = 13 + net_f = simple_net_file(self.num_output) + f = tempfile.NamedTemporaryFile(delete=False) + f.write("""net: '""" + net_f + """' + test_iter: 10 test_interval: 10 base_lr: 0.01 momentum: 0.9 + weight_decay: 0.0005 lr_policy: 'inv' gamma: 0.0001 power: 0.75 + display: 100 max_iter: 100 snapshot_after_train: false""") + f.close() + self.solver = caffe.SGDSolver(f.name) + self.solver.net.set_mode_cpu() + # fill in valid labels + self.solver.net.blobs['label'].data[...] = \ + np.random.randint(self.num_output, + size=self.solver.net.blobs['label'].data.shape) + self.solver.test_nets[0].blobs['label'].data[...] = \ + np.random.randint(self.num_output, + size=self.solver.test_nets[0].blobs['label'].data.shape) + os.remove(f.name) + os.remove(net_f) + + def test_solve(self): + self.assertEqual(self.solver.iter, 0) + self.solver.solve() + self.assertEqual(self.solver.iter, 100) + + def test_net_memory(self): + """Check that nets survive after the solver is destroyed.""" + + nets = [self.solver.net] + list(self.solver.test_nets) + self.assertEqual(len(nets), 2) + del self.solver + + total = 0 + for net in nets: + for ps in net.params.itervalues(): + for p in ps: + total += p.data.sum() + p.diff.sum() + for bl in net.blobs.itervalues(): + total += bl.data.sum() + bl.diff.sum() diff --git a/scripts/travis/travis_build_and_test.sh b/scripts/travis/travis_build_and_test.sh index dec4d097c17..53c6c341101 100755 --- a/scripts/travis/travis_build_and_test.sh +++ b/scripts/travis/travis_build_and_test.sh @@ -26,6 +26,7 @@ else $MAKE all $MAKE test $MAKE pycaffe + $MAKE pytest $MAKE warn if ! $WITH_CUDA; then $MAKE lint diff --git a/scripts/travis/travis_install.sh b/scripts/travis/travis_install.sh index e17f253ecdc..82f386cf029 100755 --- a/scripts/travis/travis_install.sh +++ b/scripts/travis/travis_install.sh @@ -57,3 +57,14 @@ $MAKE $MAKE install popd rm -f $LMDB_FILE + +# Install the Python runtime dependencies via miniconda (this is much faster +# than using pip for everything). +wget http://repo.continuum.io/miniconda/Miniconda-latest-Linux-x86_64.sh -O miniconda.sh +chmod +x miniconda.sh +./miniconda.sh -b +export PATH=/home/travis/miniconda/bin:$PATH +conda update --yes conda +conda install --yes numpy scipy matplotlib scikit-image pip +pip install protobuf +rm /home/travis/miniconda/lib/libm.* diff --git a/scripts/travis/travis_setup_makefile_config.sh b/scripts/travis/travis_setup_makefile_config.sh index a309bb3d6fe..e8d85f9be78 100755 --- a/scripts/travis/travis_setup_makefile_config.sh +++ b/scripts/travis/travis_setup_makefile_config.sh @@ -5,10 +5,18 @@ set -e mv Makefile.config.example Makefile.config if $WITH_CUDA; then - # Remove default gencode set; only generate compute_50. - sed -i 's/-gencode arch=.*\\//' Makefile.config - sed -i 's/CUDA_ARCH :=//' Makefile.config + # Only generate compute_50. GENCODE="-gencode arch=compute_50,code=sm_50" GENCODE="$GENCODE -gencode arch=compute_50,code=compute_50" echo "CUDA_ARCH := $GENCODE" >> Makefile.config fi + +cat << 'EOF' >> Makefile.config +ANACONDA_HOME := $(HOME)/miniconda +PYTHON_INCLUDE := $(ANACONDA_HOME)/include \ + $(ANACONDA_HOME)/include/python2.7 \ + $(ANACONDA_HOME)/lib/python2.7/site-packages/numpy/core/include +PYTHON_LIB := $(ANACONDA_HOME)/lib +INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include +LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib +EOF