Skip to content

Commit

Permalink
MatCaffe3 : a powerful matlab interface for caffe
Browse files Browse the repository at this point in the history
Added matcaffe3, a powerful matlab interface. To test it, run 'make mattest'
  • Loading branch information
ronghanghu committed May 29, 2015
1 parent b12c171 commit ee0c931
Show file tree
Hide file tree
Showing 20 changed files with 1,141 additions and 1 deletion.
22 changes: 21 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ NONGEN_CXX_SRCS := $(shell find \
include/$(PROJECT) \
python/$(PROJECT) \
matlab/$(PROJECT) \
matlab/+$(PROJECT)/private \
examples \
tools \
-name "*.cpp" -or -name "*.hpp" -or -name "*.cu" -or -name "*.cuh")
Expand All @@ -81,10 +82,13 @@ PY$(PROJECT)_SO := python/$(PROJECT)/_$(PROJECT).so
PY$(PROJECT)_HXX := include/$(PROJECT)/python_layer.hpp
# MAT$(PROJECT)_SRC is the matlab wrapper for $(PROJECT)
MAT$(PROJECT)_SRC := matlab/$(PROJECT)/mat$(PROJECT).cpp
# MAT$(PROJECT)_PKG_SRC is the mex entrance point of matlab package for $(PROJECT)
MAT$(PROJECT)_PKG_SRC := matlab/+$(PROJECT)/private/$(PROJECT)_.cpp
ifneq ($(MATLAB_DIR),)
MAT_SO_EXT := $(shell $(MATLAB_DIR)/bin/mexext)
endif
MAT$(PROJECT)_SO := matlab/$(PROJECT)/$(PROJECT).$(MAT_SO_EXT)
MAT$(PROJECT)_PKG_SO := matlab/+$(PROJECT)/private/$(PROJECT)_.$(MAT_SO_EXT)

##############################
# Derive generated files
Expand Down Expand Up @@ -447,7 +451,7 @@ $(PY$(PROJECT)_SO): $(PY$(PROJECT)_SRC) $(PY$(PROJECT)_HXX) | $(DYNAMIC_NAME)

mat$(PROJECT): mat

mat: $(MAT$(PROJECT)_SO)
mat: $(MAT$(PROJECT)_SO) $(MAT$(PROJECT)_PKG_SO)

$(MAT$(PROJECT)_SO): $(MAT$(PROJECT)_SRC) $(STATIC_NAME)
@ if [ -z "$(MATLAB_DIR)" ]; then \
Expand All @@ -460,13 +464,28 @@ $(MAT$(PROJECT)_SO): $(MAT$(PROJECT)_SRC) $(STATIC_NAME)
CXX="$(CXX)" \
CXXFLAGS="\$$CXXFLAGS $(MATLAB_CXXFLAGS)" \
CXXLIBS="\$$CXXLIBS $(STATIC_LINK_COMMAND) $(LDFLAGS)" -output $@

$(MAT$(PROJECT)_PKG_SO): $(MAT$(PROJECT)_PKG_SRC) $(STATIC_NAME)
@ if [ -z "$(MATLAB_DIR)" ]; then \
echo "MATLAB_DIR must be specified in $(CONFIG_FILE)" \
"to build mat$(PROJECT)."; \
exit 1; \
fi
@ echo MEX $<
$(Q)$(MATLAB_DIR)/bin/mex $(MAT$(PROJECT)_PKG_SRC) \
CXX="$(CXX)" \
CXXFLAGS="\$$CXXFLAGS $(MATLAB_CXXFLAGS)" \
CXXLIBS="\$$CXXLIBS $(STATIC_LINK_COMMAND) $(LDFLAGS)" -output $@

runtest: $(TEST_ALL_BIN)
$(TOOL_BUILD_DIR)/caffe
$(TEST_ALL_BIN) $(TEST_GPUID) --gtest_shuffle $(TEST_FILTER)

pytest: py
cd python; python -m unittest discover -s caffe/test

mattest: mat
cd matlab; $(MATLAB_DIR)/bin/matlab -nodisplay -r 'caffe.run_tests(), exit()'

warn: $(EMPTY_WARN_REPORT)

Expand Down Expand Up @@ -582,6 +601,7 @@ clean:
@- $(RM) -rf $(DISTRIBUTE_DIR)
@- $(RM) $(PY$(PROJECT)_SO)
@- $(RM) $(MAT$(PROJECT)_SO)
@- $(RM) $(MAT$(PROJECT)_PKG_SO)

supercleanfiles:
$(eval SUPERCLEAN_FILES := $(strip \
Expand Down
74 changes: 74 additions & 0 deletions matlab/+caffe/+test/test_net.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
classdef test_net < matlab.unittest.TestCase

properties
num_output
model_file
net
end

methods (Static)
function model_file = simple_net_file(num_output)
model_file = tempname();
fid = fopen(model_file, 'w');
fprintf(fid, [ ...
'name: "testnet" force_backward: true\n' ...
'layer { type: "DummyData" name: "data" top: "data" top: "label"\n' ...
'dummy_data_param { num: 5 channels: 2 height: 3 width: 4\n' ...
' num: 5 channels: 1 height: 1 width: 1\n' ...
' data_filler { type: "gaussian" std: 1 }\n' ...
' data_filler { type: "constant" } } }\n' ...
'layer { type: "Convolution" name: "conv" bottom: "data" top: "conv"\n' ...
' convolution_param { num_output: 11 kernel_size: 2 pad: 3\n' ...
' weight_filler { type: "gaussian" std: 1 }\n' ...
' bias_filler { type: "constant" value: 2 } }\n' ...
' param { decay_mult: 1 } param { decay_mult: 0 }\n' ...
' }\n' ...
'layer { type: "InnerProduct" name: "ip" bottom: "conv" top: "ip"\n' ...
' inner_product_param { num_output: ' num2str(num_output) ...
' weight_filler { type: "gaussian" std: 2.5 }\n' ...
' bias_filler { type: "constant" value: -3 } } }\n' ...
'layer { type: "SoftmaxWithLoss" name: "loss" bottom: "ip" bottom: "label"\n' ...
' top: "loss" }' ]);
fclose(fid);
end
end
methods
function self = test_net()
self.num_output = 13;
self.model_file = caffe.test.test_net.simple_net_file(self.num_output);
self.net = caffe.Net(self.model_file, 'train');
% also make sure get_solver runs
caffe.get_net(self.model_file, 'train');

% fill in valid labels
self.net.blobs('label').set_data(randi( ...
self.num_output - 1, self.net.blobs('label').shape));

delete(self.model_file);
end
end
methods (Test)
function test_forward_backward(self)
self.net.forward_prefilled();
self.net.backward_prefilled();
end
function test_inputs_outputs(self)
self.verifyEqual(self.net.inputs, cell(0, 1))
self.verifyEqual(self.net.outputs, {'loss'});
end
function test_save_and_read(self)
weights_file = tempname();
self.net.save(weights_file);
model_file2 = caffe.test.test_net.simple_net_file(self.num_output);
net2 = caffe.Net(model_file2, weights_file, 'train');
delete(model_file2);
delete(weights_file);
for l = 1:length(self.net.layer_vec)
for i = 1:length(self.net.layer_vec(l).params)
self.verifyEqual(self.net.layer_vec(l).params(i).get_data(), ...
net2.layer_vec(l).params(i).get_data());
end
end
end
end
end
43 changes: 43 additions & 0 deletions matlab/+caffe/+test/test_solver.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
classdef test_solver < matlab.unittest.TestCase

properties
num_output
solver
end

methods
function self = test_solver()
self.num_output = 13;
model_file = caffe.test.test_net.simple_net_file(self.num_output);
solver_file = tempname();

fid = fopen(solver_file, 'w');
fprintf(fid, [ ...
'net: "' model_file '"\n' ...
'test_iter: 10 test_interval: 10 base_lr: 0.01 momentum: 0.9\n' ...
'weight_decay: 0.0005 lr_policy: "inv" gamma: 0.0001 power: 0.75\n' ...
'display: 100 max_iter: 100 snapshot_after_train: false\n' ]);
fclose(fid);

self.solver = caffe.Solver(solver_file);
% also make sure get_solver runs
caffe.get_solver(solver_file);
caffe.set_mode_cpu();
% fill in valid labels
self.solver.net.blobs('label').set_data(randi( ...
self.num_output - 1, self.solver.net.blobs('label').shape));
self.solver.test_nets(1).blobs('label').set_data(randi( ...
self.num_output - 1, self.solver.test_nets(1).blobs('label').shape));

delete(solver_file);
delete(model_file);
end
end
methods (Test)
function test_solve(self)
self.verifyEqual(self.solver.iter(), 0)
self.solver.solve()
self.verifyEqual(self.solver.iter(), 100)
end
end
end
71 changes: 71 additions & 0 deletions matlab/+caffe/Blob.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
classdef Blob < handle
% Wrapper class of caffe::Blob in matlab

properties (Access = private)
hBlob_self
end

methods
function self = Blob(hBlob_blob)
CHECK(is_valid_handle(hBlob_blob), 'invalid input handle');

% setup self handle and attributes
self.hBlob_self = hBlob_blob;
end
function shape = shape(self)
shape = caffe_('blob_get_shape', self.hBlob_self);
end
function reshape(self, shape)
shape = self.check_and_preprocess_shape(shape);
caffe_('blob_reshape', self.hBlob_self, shape);
end
function data = get_data(self)
data = caffe_('blob_get_data', self.hBlob_self);
end
function set_data(self, data)
data = self.check_and_preprocess_data(data);
caffe_('blob_set_data', self.hBlob_self, data);
end
function diff = get_diff(self)
diff = caffe_('blob_get_diff', self.hBlob_self);
end
function set_diff(self, diff)
diff = self.check_and_preprocess_data(diff);
caffe_('blob_set_diff', self.hBlob_self, diff);
end
end

methods (Access = private)
function shape = check_and_preprocess_shape(~, shape)
CHECK(isempty(shape) || isnumeric(shape) && isrow(shape), ...
'shape must be a integer row vector');
shape = double(shape);
end
function data = check_and_preprocess_data(self, data)
CHECK(isnumeric(data), 'data or diff must be numeric types');
self.check_data_size_matches(data)
data = single(data);
end
function check_data_size_matches(self, data)
% check whether size of data matches shape of this blob
% note: matlab arrays always have at least 2 dimensions. To compare
% shape between size of data and shape of this blob, extend shape of
% this blob to have at least 2 dimensions
data_size = size(data);
self_shape_extended = self.shape;
if isempty(self_shape_extended)
% target blob is a scalar (0 dim)
self_shape_extended = [1, 1];
elseif isscalar(self_shape_extended)
% target blob is a vector (1 dim)
self_shape_extended = [self_shape_extended, 1];
end
is_matched = (length(self_shape_extended) == length(data_size)) ...
&& all(self_shape_extended == data_size);
CHECK(is_matched, ...
sprintf('%s, data size: [ %s], blob shape: [ %s]', ...
'data size does not match blob shape', ...
sprintf('%d ', data_size), sprintf('%d ', self_shape_extended)));
end
end
end
32 changes: 32 additions & 0 deletions matlab/+caffe/Layer.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
classdef Layer < handle
% Wrapper class of caffe::Layer in matlab

properties (Access = private)
hLayer_self
attributes
% attributes fields:
% hBlob_blobs
end
properties (SetAccess = private)
params
end

methods
function self = Layer(hLayer_layer)
CHECK(is_valid_handle(hLayer_layer), 'invalid input handle');

% setup self handle and attributes
self.hLayer_self = hLayer_layer;
self.attributes = caffe_('layer_get_attr', self.hLayer_self);

% setup weights
self.params = caffe.Blob.empty();
for n = 1:length(self.attributes.hBlob_blobs)
self.params(n) = caffe.Blob(self.attributes.hBlob_blobs(n));
end
end
function layer_type = type(self)
layer_type = caffe_('layer_get_type', self.hLayer_self);
end
end
end
Loading

0 comments on commit ee0c931

Please sign in to comment.