rnn结构的特点是,上一次的forward的输出会作为这次forward的输入,这种反馈结构在目前的layer设计要体现,同时rnn也存在很多变种,内部结构多种多样,比如GRU LSTM
如何在保证反馈结构的同时,容许更加容易的实现内部结构?
caffe中使用net去代替了layer,因为net内部又能用layer表述更加细致的结构变化,在net层面上实现反馈的输入即可,因此源码设计上,RecurrentLayer是个虚类,
LSTM是个子类,在RecurrentLayer中
/// @brief A Net to implement the Recurrent functionality.
shared_ptr<Net<Dtype> > unrolled_net_;
同时为了反馈计算,定义了:
vector<Blob<Dtype>* > recur_input_blobs_;
vector<Blob<Dtype>* > recur_output_blobs_;
在forword之前:
const Dtype* timestep_T_data = recur_output_blobs_[i]->cpu_data();
Dtype* timestep_0_data = recur_input_blobs_[i]->mutable_cpu_data();
caffe_copy(count, timestep_T_data, timestep_0_data);
将net top的数据拷贝到net bottom上来,也就是output到input的反馈
然后再前向计算:
unrolled_net_->ForwardTo(last_layer_index_);
在RecurrentLayer<Dtype>::LayerSetUp中,指定了
T_ = bottom[0]->shape(0);
N_ = bottom[0]->shape(1);
T_表示序列的长度 N_表示通道的数量。
LSTM都是2个输入吗?
从代码来看,必须是
CHECK_EQ(bottom[1]->num_axes(), 2)
<< "bottom[1] must have exactly 2 axes -- (#timesteps, #streams)";
CHECK_EQ(T_, bottom[1]->shape(0));
CHECK_EQ(N_, bottom[1]->shape(1));
在每一次前向计算,都会传递
template <typename Dtype>
void RecurrentLayer<Dtype>::Reshape(const vector<Blob<Dtype>*>& bottom,
const vector<Blob<Dtype>*>& top) {
CHECK_EQ(top.size(), output_blobs_.size());
for (int i = 0; i < top.size(); ++i) {
top[i]->ReshapeLike(*output_blobs_[i]);
output_blobs_[i]->ShareData(*top[i]);
output_blobs_[i]->ShareDiff(*top[i]);
}
x_input_blob_->ShareData(*bottom[0]);
x_input_blob_->ShareDiff(*bottom[0]);
cont_input_blob_->ShareData(*bottom[1]);
if (static_input_) {
x_static_input_blob_->ShareData(*bottom[2]);
x_static_input_blob_->ShareDiff(*bottom[2]);
}
}