我正在尝试使用Caffe在Python中实现简单的损失层,但尝试不成功。我找到了几个已经用Python实现的参考层,包括这里、这里和这里。从Caffe文档/示例提供的EuclideanLossLayer开始,我无法使其工作并进行了调试。即使是使用这个简单的TestLayer:
将数据写入LMDB应该是正确的,因为使用Caffe提供的MNIST数据集进行测试时没有出现问题。网络结构定义如下:
手动解决问题需要使用以下步骤:
相应的prototxt文件如下: :
def setup(self, bottom, top):
"""
Checks the correct number of bottom inputs.
:param bottom: bottom inputs
:type bottom: [numpy.ndarray]
:param top: top outputs
:type top: [numpy.ndarray]
"""
print 'setup'
def reshape(self, bottom, top):
"""
Make sure all involved blobs have the right dimension.
:param bottom: bottom inputs
:type bottom: caffe._caffe.RawBlobVec
:param top: top outputs
:type top: caffe._caffe.RawBlobVec
"""
print 'reshape'
top[0].reshape(bottom[0].data.shape[0], bottom[0].data.shape[1], bottom[0].data.shape[2], bottom[0].data.shape[3])
def forward(self, bottom, top):
"""
Forward propagation.
:param bottom: bottom inputs
:type bottom: caffe._caffe.RawBlobVec
:param top: top outputs
:type top: caffe._caffe.RawBlobVec
"""
print 'forward'
top[0].data[...] = bottom[0].data
def backward(self, top, propagate_down, bottom):
"""
Backward pass.
:param bottom: bottom inputs
:type bottom: caffe._caffe.RawBlobVec
:param propagate_down:
:type propagate_down:
:param top: top outputs
:type top: caffe._caffe.RawBlobVec
"""
print 'backward'
bottom[0].diff[...] = top[0].diff[...]
我无法让Python层正常工作。学习任务相对简单,我只是试图预测一个实数是正数还是负数。相应的数据如下生成并写入LMDBs:
N = 10000
N_train = int(0.8*N)
images = []
labels = []
for n in range(N):
image = (numpy.random.rand(1, 1, 1)*2 - 1).astype(numpy.float)
label = int(numpy.sign(image))
images.append(image)
labels.append(label)
将数据写入LMDB应该是正确的,因为使用Caffe提供的MNIST数据集进行测试时没有出现问题。网络结构定义如下:
net.data, net.labels = caffe.layers.Data(batch_size = batch_size, backend = caffe.params.Data.LMDB,
source = lmdb_path, ntop = 2)
net.fc1 = caffe.layers.Python(net.data, python_param = dict(module = 'tools.layers', layer = 'TestLayer'))
net.score = caffe.layers.TanH(net.fc1)
net.loss = caffe.layers.EuclideanLoss(net.score, net.labels)
手动解决问题需要使用以下步骤:
for iteration in range(iterations):
solver.step(step)
相应的prototxt文件如下: :
weight_decay: 0.0005
test_net: "tests/test.prototxt"
snapshot_prefix: "tests/snapshot_"
max_iter: 1000
stepsize: 1000
base_lr: 0.01
snapshot: 0
gamma: 0.01
solver_mode: CPU
train_net: "tests/train.prototxt"
test_iter: 0
test_initialization: false
lr_policy: "step"
momentum: 0.9
display: 100
test_interval: 100000
train.prototxt
:
layer {
name: "data"
type: "Data"
top: "data"
top: "labels"
data_param {
source: "tests/train_lmdb"
batch_size: 64
backend: LMDB
}
}
layer {
name: "fc1"
type: "Python"
bottom: "data"
top: "fc1"
python_param {
module: "tools.layers"
layer: "TestLayer"
}
}
layer {
name: "score"
type: "TanH"
bottom: "fc1"
top: "score"
}
layer {
name: "loss"
type: "EuclideanLoss"
bottom: "score"
bottom: "labels"
top: "loss"
}
test.prototxt
:
layer {
name: "data"
type: "Data"
top: "data"
top: "labels"
data_param {
source: "tests/test_lmdb"
batch_size: 64
backend: LMDB
}
}
layer {
name: "fc1"
type: "Python"
bottom: "data"
top: "fc1"
python_param {
module: "tools.layers"
layer: "TestLayer"
}
}
layer {
name: "score"
type: "TanH"
bottom: "fc1"
top: "score"
}
layer {
name: "loss"
type: "EuclideanLoss"
bottom: "score"
bottom: "labels"
top: "loss"
}
我试图追踪它,向TestLayer
的backward
和foward
方法中添加调试信息,但只有在解决过程中才会调用forward
方法(请注意,这里没有执行任何测试,这些调用只能与解决相关)。同样地,在python_layer.hpp
中添加了调试信息:
virtual void Forward_cpu(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
LOG(INFO) << "cpp forward";
self_.attr("forward")(bottom, top);
}
virtual void Backward_cpu(const vector<Blob<Dtype>*>& top,
const vector<bool>& propagate_down, const vector<Blob<Dtype>*>& bottom) {
LOG(INFO) << "cpp backward";
self_.attr("backward")(top, propagate_down, bottom);
}
再次强调,只有前向传递被执行。当我删除TestLayer
中的backward
方法时,求解仍然有效。但是如果删除forward
方法,会出现错误,提示forward
未实现。我本应该期望对于backward
也是同样的情况,因此似乎根本没有执行反向传递。切换回常规层并添加调试消息,一切都按预期工作。
我觉得可能是我缺少了一些简单或基本的知识,但是我几天来一直无法解决这个问题。所以任何帮助或提示都将不胜感激。
谢谢!
diff
(如果是这样,那么所有梯度也都被重置了)。在每个backward()
调用之后设置net.blobs['prob'].diff[0, 298] = 1
是否可以解决它? - Yamanekonet.blobs['prob'].diff[0, 298] = 1
,由其本质保证了它的值仍然为1。我的担忧是,如果Caffe在每次迭代后重置'diff'(如您所说),那么在net.backward()
之后就无法从net.blobs[layer_name].diff
访问grad。此外,如果在net.backward()
之后访问net.blobs[layer_name].diff
是正确的方法,那么最顶层的概率层prob
(net.blobs['prob'].diff
)的梯度应该保持不变(例如net.blobs['prob'].diff[0, 298] = 1
),因为梯度计算从prob
层开始。 - Stonenet.blobs[layer].diff
是在backward()
之后。然后,我不明白为什么它会重置prob
的差分机制。如果你发现了什么,请告诉我。 - Yamanekobackward()
后的diff
,那么我可能错过了一些配置。谢谢! - Stone