Skip to content

Commit ee0c931

Browse files
committed
MatCaffe3 : a powerful matlab interface for caffe
Added matcaffe3, a powerful matlab interface. To test it, run 'make mattest'
1 parent b12c171 commit ee0c931

20 files changed

Lines changed: 1141 additions & 1 deletion

Makefile

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ NONGEN_CXX_SRCS := $(shell find \
6666
include/$(PROJECT) \
6767
python/$(PROJECT) \
6868
matlab/$(PROJECT) \
69+
matlab/+$(PROJECT)/private \
6970
examples \
7071
tools \
7172
-name "*.cpp" -or -name "*.hpp" -or -name "*.cu" -or -name "*.cuh")
@@ -81,10 +82,13 @@ PY$(PROJECT)_SO := python/$(PROJECT)/_$(PROJECT).so
8182
PY$(PROJECT)_HXX := include/$(PROJECT)/python_layer.hpp
8283
# MAT$(PROJECT)_SRC is the matlab wrapper for $(PROJECT)
8384
MAT$(PROJECT)_SRC := matlab/$(PROJECT)/mat$(PROJECT).cpp
85+
# MAT$(PROJECT)_PKG_SRC is the mex entrance point of matlab package for $(PROJECT)
86+
MAT$(PROJECT)_PKG_SRC := matlab/+$(PROJECT)/private/$(PROJECT)_.cpp
8487
ifneq ($(MATLAB_DIR),)
8588
MAT_SO_EXT := $(shell $(MATLAB_DIR)/bin/mexext)
8689
endif
8790
MAT$(PROJECT)_SO := matlab/$(PROJECT)/$(PROJECT).$(MAT_SO_EXT)
91+
MAT$(PROJECT)_PKG_SO := matlab/+$(PROJECT)/private/$(PROJECT)_.$(MAT_SO_EXT)
8892

8993
##############################
9094
# Derive generated files
@@ -447,7 +451,7 @@ $(PY$(PROJECT)_SO): $(PY$(PROJECT)_SRC) $(PY$(PROJECT)_HXX) | $(DYNAMIC_NAME)
447451

448452
mat$(PROJECT): mat
449453

450-
mat: $(MAT$(PROJECT)_SO)
454+
mat: $(MAT$(PROJECT)_SO) $(MAT$(PROJECT)_PKG_SO)
451455

452456
$(MAT$(PROJECT)_SO): $(MAT$(PROJECT)_SRC) $(STATIC_NAME)
453457
@ if [ -z "$(MATLAB_DIR)" ]; then \
@@ -460,13 +464,28 @@ $(MAT$(PROJECT)_SO): $(MAT$(PROJECT)_SRC) $(STATIC_NAME)
460464
CXX="$(CXX)" \
461465
CXXFLAGS="\$$CXXFLAGS $(MATLAB_CXXFLAGS)" \
462466
CXXLIBS="\$$CXXLIBS $(STATIC_LINK_COMMAND) $(LDFLAGS)" -output $@
467+
468+
$(MAT$(PROJECT)_PKG_SO): $(MAT$(PROJECT)_PKG_SRC) $(STATIC_NAME)
469+
@ if [ -z "$(MATLAB_DIR)" ]; then \
470+
echo "MATLAB_DIR must be specified in $(CONFIG_FILE)" \
471+
"to build mat$(PROJECT)."; \
472+
exit 1; \
473+
fi
474+
@ echo MEX $<
475+
$(Q)$(MATLAB_DIR)/bin/mex $(MAT$(PROJECT)_PKG_SRC) \
476+
CXX="$(CXX)" \
477+
CXXFLAGS="\$$CXXFLAGS $(MATLAB_CXXFLAGS)" \
478+
CXXLIBS="\$$CXXLIBS $(STATIC_LINK_COMMAND) $(LDFLAGS)" -output $@
463479

464480
runtest: $(TEST_ALL_BIN)
465481
$(TOOL_BUILD_DIR)/caffe
466482
$(TEST_ALL_BIN) $(TEST_GPUID) --gtest_shuffle $(TEST_FILTER)
467483

468484
pytest: py
469485
cd python; python -m unittest discover -s caffe/test
486+
487+
mattest: mat
488+
cd matlab; $(MATLAB_DIR)/bin/matlab -nodisplay -r 'caffe.run_tests(), exit()'
470489

471490
warn: $(EMPTY_WARN_REPORT)
472491

@@ -582,6 +601,7 @@ clean:
582601
@- $(RM) -rf $(DISTRIBUTE_DIR)
583602
@- $(RM) $(PY$(PROJECT)_SO)
584603
@- $(RM) $(MAT$(PROJECT)_SO)
604+
@- $(RM) $(MAT$(PROJECT)_PKG_SO)
585605

586606
supercleanfiles:
587607
$(eval SUPERCLEAN_FILES := $(strip \

matlab/+caffe/+test/test_net.m

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
classdef test_net < matlab.unittest.TestCase
2+
3+
properties
4+
num_output
5+
model_file
6+
net
7+
end
8+
9+
methods (Static)
10+
function model_file = simple_net_file(num_output)
11+
model_file = tempname();
12+
fid = fopen(model_file, 'w');
13+
fprintf(fid, [ ...
14+
'name: "testnet" force_backward: true\n' ...
15+
'layer { type: "DummyData" name: "data" top: "data" top: "label"\n' ...
16+
'dummy_data_param { num: 5 channels: 2 height: 3 width: 4\n' ...
17+
' num: 5 channels: 1 height: 1 width: 1\n' ...
18+
' data_filler { type: "gaussian" std: 1 }\n' ...
19+
' data_filler { type: "constant" } } }\n' ...
20+
'layer { type: "Convolution" name: "conv" bottom: "data" top: "conv"\n' ...
21+
' convolution_param { num_output: 11 kernel_size: 2 pad: 3\n' ...
22+
' weight_filler { type: "gaussian" std: 1 }\n' ...
23+
' bias_filler { type: "constant" value: 2 } }\n' ...
24+
' param { decay_mult: 1 } param { decay_mult: 0 }\n' ...
25+
' }\n' ...
26+
'layer { type: "InnerProduct" name: "ip" bottom: "conv" top: "ip"\n' ...
27+
' inner_product_param { num_output: ' num2str(num_output) ...
28+
' weight_filler { type: "gaussian" std: 2.5 }\n' ...
29+
' bias_filler { type: "constant" value: -3 } } }\n' ...
30+
'layer { type: "SoftmaxWithLoss" name: "loss" bottom: "ip" bottom: "label"\n' ...
31+
' top: "loss" }' ]);
32+
fclose(fid);
33+
end
34+
end
35+
methods
36+
function self = test_net()
37+
self.num_output = 13;
38+
self.model_file = caffe.test.test_net.simple_net_file(self.num_output);
39+
self.net = caffe.Net(self.model_file, 'train');
40+
% also make sure get_solver runs
41+
caffe.get_net(self.model_file, 'train');
42+
43+
% fill in valid labels
44+
self.net.blobs('label').set_data(randi( ...
45+
self.num_output - 1, self.net.blobs('label').shape));
46+
47+
delete(self.model_file);
48+
end
49+
end
50+
methods (Test)
51+
function test_forward_backward(self)
52+
self.net.forward_prefilled();
53+
self.net.backward_prefilled();
54+
end
55+
function test_inputs_outputs(self)
56+
self.verifyEqual(self.net.inputs, cell(0, 1))
57+
self.verifyEqual(self.net.outputs, {'loss'});
58+
end
59+
function test_save_and_read(self)
60+
weights_file = tempname();
61+
self.net.save(weights_file);
62+
model_file2 = caffe.test.test_net.simple_net_file(self.num_output);
63+
net2 = caffe.Net(model_file2, weights_file, 'train');
64+
delete(model_file2);
65+
delete(weights_file);
66+
for l = 1:length(self.net.layer_vec)
67+
for i = 1:length(self.net.layer_vec(l).params)
68+
self.verifyEqual(self.net.layer_vec(l).params(i).get_data(), ...
69+
net2.layer_vec(l).params(i).get_data());
70+
end
71+
end
72+
end
73+
end
74+
end

matlab/+caffe/+test/test_solver.m

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
classdef test_solver < matlab.unittest.TestCase
2+
3+
properties
4+
num_output
5+
solver
6+
end
7+
8+
methods
9+
function self = test_solver()
10+
self.num_output = 13;
11+
model_file = caffe.test.test_net.simple_net_file(self.num_output);
12+
solver_file = tempname();
13+
14+
fid = fopen(solver_file, 'w');
15+
fprintf(fid, [ ...
16+
'net: "' model_file '"\n' ...
17+
'test_iter: 10 test_interval: 10 base_lr: 0.01 momentum: 0.9\n' ...
18+
'weight_decay: 0.0005 lr_policy: "inv" gamma: 0.0001 power: 0.75\n' ...
19+
'display: 100 max_iter: 100 snapshot_after_train: false\n' ]);
20+
fclose(fid);
21+
22+
self.solver = caffe.Solver(solver_file);
23+
% also make sure get_solver runs
24+
caffe.get_solver(solver_file);
25+
caffe.set_mode_cpu();
26+
% fill in valid labels
27+
self.solver.net.blobs('label').set_data(randi( ...
28+
self.num_output - 1, self.solver.net.blobs('label').shape));
29+
self.solver.test_nets(1).blobs('label').set_data(randi( ...
30+
self.num_output - 1, self.solver.test_nets(1).blobs('label').shape));
31+
32+
delete(solver_file);
33+
delete(model_file);
34+
end
35+
end
36+
methods (Test)
37+
function test_solve(self)
38+
self.verifyEqual(self.solver.iter(), 0)
39+
self.solver.solve()
40+
self.verifyEqual(self.solver.iter(), 100)
41+
end
42+
end
43+
end

matlab/+caffe/Blob.m

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
classdef Blob < handle
2+
% Wrapper class of caffe::Blob in matlab
3+
4+
properties (Access = private)
5+
hBlob_self
6+
end
7+
8+
methods
9+
function self = Blob(hBlob_blob)
10+
CHECK(is_valid_handle(hBlob_blob), 'invalid input handle');
11+
12+
% setup self handle and attributes
13+
self.hBlob_self = hBlob_blob;
14+
end
15+
function shape = shape(self)
16+
shape = caffe_('blob_get_shape', self.hBlob_self);
17+
end
18+
function reshape(self, shape)
19+
shape = self.check_and_preprocess_shape(shape);
20+
caffe_('blob_reshape', self.hBlob_self, shape);
21+
end
22+
function data = get_data(self)
23+
data = caffe_('blob_get_data', self.hBlob_self);
24+
end
25+
function set_data(self, data)
26+
data = self.check_and_preprocess_data(data);
27+
caffe_('blob_set_data', self.hBlob_self, data);
28+
end
29+
function diff = get_diff(self)
30+
diff = caffe_('blob_get_diff', self.hBlob_self);
31+
end
32+
function set_diff(self, diff)
33+
diff = self.check_and_preprocess_data(diff);
34+
caffe_('blob_set_diff', self.hBlob_self, diff);
35+
end
36+
end
37+
38+
methods (Access = private)
39+
function shape = check_and_preprocess_shape(~, shape)
40+
CHECK(isempty(shape) || isnumeric(shape) && isrow(shape), ...
41+
'shape must be a integer row vector');
42+
shape = double(shape);
43+
end
44+
function data = check_and_preprocess_data(self, data)
45+
CHECK(isnumeric(data), 'data or diff must be numeric types');
46+
self.check_data_size_matches(data)
47+
data = single(data);
48+
end
49+
function check_data_size_matches(self, data)
50+
% check whether size of data matches shape of this blob
51+
% note: matlab arrays always have at least 2 dimensions. To compare
52+
% shape between size of data and shape of this blob, extend shape of
53+
% this blob to have at least 2 dimensions
54+
data_size = size(data);
55+
self_shape_extended = self.shape;
56+
if isempty(self_shape_extended)
57+
% target blob is a scalar (0 dim)
58+
self_shape_extended = [1, 1];
59+
elseif isscalar(self_shape_extended)
60+
% target blob is a vector (1 dim)
61+
self_shape_extended = [self_shape_extended, 1];
62+
end
63+
is_matched = (length(self_shape_extended) == length(data_size)) ...
64+
&& all(self_shape_extended == data_size);
65+
CHECK(is_matched, ...
66+
sprintf('%s, data size: [ %s], blob shape: [ %s]', ...
67+
'data size does not match blob shape', ...
68+
sprintf('%d ', data_size), sprintf('%d ', self_shape_extended)));
69+
end
70+
end
71+
end

matlab/+caffe/Layer.m

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
classdef Layer < handle
2+
% Wrapper class of caffe::Layer in matlab
3+
4+
properties (Access = private)
5+
hLayer_self
6+
attributes
7+
% attributes fields:
8+
% hBlob_blobs
9+
end
10+
properties (SetAccess = private)
11+
params
12+
end
13+
14+
methods
15+
function self = Layer(hLayer_layer)
16+
CHECK(is_valid_handle(hLayer_layer), 'invalid input handle');
17+
18+
% setup self handle and attributes
19+
self.hLayer_self = hLayer_layer;
20+
self.attributes = caffe_('layer_get_attr', self.hLayer_self);
21+
22+
% setup weights
23+
self.params = caffe.Blob.empty();
24+
for n = 1:length(self.attributes.hBlob_blobs)
25+
self.params(n) = caffe.Blob(self.attributes.hBlob_blobs(n));
26+
end
27+
end
28+
function layer_type = type(self)
29+
layer_type = caffe_('layer_get_type', self.hLayer_self);
30+
end
31+
end
32+
end

0 commit comments

Comments
 (0)