以下内容介绍了如何使用Matlab去使用caffe, 翻译自http://caffe.berkeleyvision.org/tutorial/interfaces.html#matlab, 纯手打,如果错误还望指正.

Matlab

caffe的Matlab接口叫matcaffe, 程序包放在caffe/matlab中,你可以利用它在你的Matlab代码中调用caffe的功能.
包括以下:

  • 在Matlab中创建多个Nets
  • 前向后向计算
  • 访问network中任意层 任意参数
  • 编辑修改网络参数及其他
  • 在Matlab中创建多种Solvers来训练网络
  • 从snapshots中恢复训练网络
  • 访问solver中的训练网络和测试网络
  • 可以在Matlab后台操作运行指定迭代次数
  • 用Matlab代码写梯度下降操作

caffe/matlab/demo/calssification_demo.m中有个ILSVRC图像分类的demo,你可以从Model Zoo中下载CaffeNet然后去尝试运行它.

编译MatCaffe

运行命令 make all matcaffe 就可以编译matcaffe, 然后你可以运行 make mattest去测试一下编译是否成功.

常见的问题:如果你测试的时候出现如"libstdc++.so.6:version 'GLIBCXX_3.4.15 not found’这样的报错的话, 这通常意味着你Matlab的runtime libraries和compile-time libraries不匹配. 你需要命令行运行以下两行代码然后重启Matlab:

export LD_LIBRARY_PATH=/opt/intel/mkl/lib/intel64:/usr/local/cuda/lib64
 export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libstdc++.so.6

重新运行 make mattest测试看问题解决了没有.注意:这个问题有时候会比较复杂,因为Matlab启动时可能会重写你的LD_LIBRARY_PATH环境变量.这样的话,你可以在matlab中运行 !ldd ./matlab/+caffe/private/caffe_.mexa64(mex扩展名可能跟你的系统不一样,自己看)来看它的runtime libraries, 然后通过export你的compile-time libraries 到LD_PRELOAD环境变量来预加载他们.

成功编译和测试之后, 从caffe根目录启动Matalb,然后将matcaffe程序包加入到Matlab搜索路径,即运行以下命令:

addpath ./matlab
使用Matcaffe

Matcaffe使用方法跟PyCaffe很类似.
下面的例子说明了具体的使用方法(假设你已经下载了CaffeNet,并且matlab当前运行目录是在caffe根目录下)

model = ‘./models/bvlc_reference_caffenet/deploy.prototxt’;
 weights = ‘./models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel’;

mode和device需要在你创建net或者solver之前就设置好
使用CPU:

caffe.set_mode_cpu();

使用GPU,指定gpu_id:

caffe.set_mode_gpu();
 caffe.set_device(gpu_id);

创建一个网络:

net = caffe.Net(model, weights, ‘test’); %创建网络并载入权重

或者

net = caffe.Net(model, ‘test’); % 创建网络但不加载权重
 net.copy_from(weights); % 加载权重

得到的net对象如下:

Net with properties:
 layer_vec: [1x23 caffe.Layer]
 blob_vec: [1x15 caffe.Blob]
 inputs: {‘data’}
 outputs: {‘prob’}
 name2layer_index: [23x1 containers.Map]
 name2blob_index: [15x1 containers.Map]
 layer_names: {23x1 cell}
 blob_names: {15x1 cell}

现在我们可以访问网络中的任意blob,比如可以用全1矩阵填充blob ‘data’:

net.blobs(‘data’).set_data(ones(net.blobs(‘data’).shape));

也可以把’data’中的值全乘以10:

net.blobs(‘data’).set_data(net.blobs(‘data’).get_data() * 10);

注意:因为Maltab是1-indexed和列优先的,所以通常blob的4个维度在Matlab中是[width, height, channels, num]这样排列的, width是变化最快的那个维度. 而且caffe的图片的颜色通道是BGR形式. 另外, Caffe使用单精度浮点型数据,如果你的数据不是single的, set_data会自动把它转成single进行运算.

你可以访问并修改任意层的参数:

net.params(‘conv1’, 1).set_data(net.params(‘conv1’, 1).get_data() * 10); % set weights
 net.params(‘conv1’, 2).set_data(net.params(‘conv1’, 2).get_data() * 10); % set bias

或者

net.layers(‘conv1’).params(1).set_data(net.layers(‘conv1’).params(1).get_data() * 10);
 net.layers(‘conv1’).params(2).set_data(net.layers(‘conv1’).params(2).get_data() * 10);

保存修改后的网络:

net.save(‘my_net.caffemodel’)

获得某个层的类型:

layer_type = net.layers(‘conv1’).type;
前向后向计算

你可以使用net.forward或者net.forward_prefilled进行前向计算, 函数net.forward接收一个N-D单元矩阵作为输入blob(s),然后输出一个包含output blob(s)数据的单元矩阵. 函数net.forward_prefilled使用input blobs(s)已经存在的数据进行前向计算,没输入没输出. 举例说明, 你可以先这样生成随机数据作为input blobs: data = rand(net.blobs(‘data’).shape), 然后运行:

res = net.forward({data});
 prob = res{1};

或者

net.blobs(‘data’).set_data(data);
 net.forward_prefilled();
 prob = net.blobs(‘prob’).get_data();

后向计算也是类似的,用net.backward或者net.backward_prefilled,但不用get_data和set_data而是get_diff和set_diff. 同样地,为output blosbs生成一些梯度值:prob_diff = rand(net.blobs(‘prob’).shape),然后就可以运行

res = net.backward({prob_diff});
 data_diff = res{1};

或者

net.blobs(‘prob’).set_diff(prob_diff);
 net.backward_prefilled();
 data_diff = net.blobs(‘data’).get_diff();

然而, 上面的后向传播计算并不能得到正确的结果,因为Caffe决定了网络不需要后向计算.为了获得正确的后向计算记过,你需要在你的网络prototxt中设置’force_backward: true’.
(后向计算这部分翻译有可能出错,因为很少会在Matlab中用backward,所以没去仔细查阅资料)

前向后向计算之后,你可以从内部blobs中获取你想要的数据或梯度,比如说,提取pool5层的特征:

pool5_feat = net.blobs(‘pool5’).get_data();
Reshape

假设你想要一次运行1张图片而不是10张:

net.blobs(‘data’).reshape([227 227 3 1]); % reshape blob ‘data’
 net.reshape();

然后整个网络就都reshaped了,现在net.blobs(‘prob’).shape应该是[1000 1]了;

训练

假设你已经按照ImageNet Tutorial的指示生成了训练和校验数据的lmdbs, 下面生成一个solver然后在ILSVRC 2012 分类数据集上进行训练:

solver = caffe.Solver(’./models/bvlc_reference_caffenet/solver.prototxt’);

生成的solver对象如下:

net.blobs(‘data’).reshape([227 227 3 1]); % reshape blob ‘data’
 net.reshape();

训练

solver.solve();

或者只训练1000个迭代(这样你可以在继续训练更多次迭代之前做一些操作)

solver.step(1000);

获取迭代次数:

iter = solver.iter();

获取网络:

train_net = solver.net;
 test_net = solver.test_nets(1);

从snapshot恢复训练:

solver.restore(‘your_snapshot.solverstate’);
输入和输出

caffe.io类提供了基本的输入函数load_image和read_mean函数. 比如, 读取ILSVRC 2012 mean file可以这么做:

mean_data = caffe.io.read_mean(’./data/ilsvrc12/imagenet_mean.binaryproto’);

读取Caffe示例图片并resize到[width, height]大小(假设希望width = 256, height = 256):

im_data = caffe.io.load_image(’./examples/images/cat.jpg’);
 im_data = imresize(im_data, [width, height]);

注意:用上面的方法读取的图片是[width height channels]的BGR形式的, 和caffe内部计算风格一样.
但和Matlab存储和读取图片的方式不一样,如果你想要用matlab函数imread来读取的话,需要做一些处理:

im_data = imread(’./examples/images/cat.jpg’); % 读取图片
 im_data = im_data(:, :, [3, 2, 1]); % 从RGB 转成BGR
 im_data = permute(im_data, [2, 1, 3]); % 转置让width和height调换
 im_data = single(im_data); % 转成single类型

另外你可以查看caffe/matlab/demo/classification_demo.m去了解怎么对图片做预处理.

Clear nets and solvers

调用caffe.reset_all()来清除所有solvers和生成的nets.