lstm.layer代码注释,(不会设置评论,有问题直接私信)

来源:互联网 发布:白银模拟交易软件 编辑:程序博客网 时间:2024/06/08 03:28
#include <string>
#include <vector>

#include "caffe/blob.hpp"
#include "caffe/common.hpp"
#include "caffe/filler.hpp"
#include "caffe/layer.hpp"
#include "caffe/sequence_layers.hpp"
#include "caffe/util/math_functions.hpp"
//一个lstm层 在内部实现的时候,是展开成了一个网络,h_ tm1s 是其中一个网络层用到的blob的名字,add_bottom是cont_h_param这个指针
namespace caffe {//命名空间,以下都针对caffe用的//cont_h_param指针的类型是LayerParamete,LayerParameter 这个定义在 caffe.proto,所以你要找add_bottom ,要去找caffe.proto 编译后得到的 源文件 文件里面
template <typename Dtype>//那源文件是protobuf生成的
void LSTMLayer<Dtype>::RecurrentInputBlobNames(vector<string>* names)const {//输入blob?
names->resize(2);ize(2);
(*names)[0] = "h_0";
(*names)[1] = "c_0"; //h_0,c_0代表LSTM中的state,表示下层的隐含输出和上层的控制状态
}

template <typename Dtype>
void LSTMLayer<Dtype>::RecurrentOutputBlobNames(vector<string>* names)const {//输出blob?
names->resize(2);
(*names)[0] = "h_" + this->int_to_str(this->T_);// 创建一个流把(t-1)值传递入流中。h带_应该是数据吧
(*names)[1] = "c_T";
}

template <typename Dtype>
void LSTMLayer<Dtype>::OutputBlobNames(vector<string>* names)const {//输出blob的名字?
names->resize(1);
(*names)[0] = "h";
}

template <typename Dtype>//从这开始往下全是↓
void LSTMLayer<Dtype>::FillUnrolledNet(NetParameter* net_param)const {
const int num_output =this->layer_param_.recurrent_param().num_output();//layer_param_是LayerParameter类
CHECK_GT(num_output, 0) << "num_output must be positive"; //CHECK_GT断言宏检验num_output是否大于0
const FillerParameter& weight_filler = //FillerParameter::FillerParameter()
this->layer_param_.recurrent_param().weight_filler();// : ::google::protobuf::Message() { optional FillerParameter weight_filler = 2;
const FillerParameter& bias_filler = // SharedCtor();
this->layer_param_.recurrent_param().bias_filler();// optional RecurrentParameter recurrent_param = 134;

// Add generic LayerParameter's (without bottoms/tops) of layer types we'll
// use to save redundant code.
LayerParameter hidden_param; //proto里的LayerParameter是一个类,hidden_param类型是LayerParameter
hidden_param.set_type("InnerProduct");
hidden_param.mutable_inner_product_param()->set_num_output(num_output *4);//网络的输出数估计是256 * 4。如果括号里是数字,按那就是set_num_output(1024)
hidden_param.mutable_inner_product_param()->set_bias_term(false);//if (bias_term_) {this->blobs_.resize(2); } else { this->blobs_.resize(1);}.没添加偏置?
hidden_param.mutable_inner_product_param()->set_axis(2);//这个2维可能一个是xxx权重W,一个是x偏x重xbX.一个是流,一个是T。0:16frame,1:stream,2:4096
hidden_param.mutable_inner_product_param()->
mutable_weight_filler()->CopyFrom(weight_filler);//weight_filler是prototxt Net里的参数

LayerParameter biased_hidden_param(hidden_param);//父类是hidden_param?
biased_hidden_param.mutable_inner_product_param()->set_bias_term(true);//又有偏置b了。
biased_hidden_param.mutable_inner_product_param()->//没设置set_axis(2);应该是默认第2维.
mutable_bias_filler()->CopyFrom(bias_filler);//bias_filler是prototxt Net里的参数,这里是把偏置好的复制过来用了
//估计是为了省计算吧。那初始化岂不是都一样?
LayerParameter sum_param;
sum_param.set_type("Eltwise");//caffe里的层,操作有三个:product(点乘), sum(相加减,默认) 和 max(取大值)
sum_param.mutable_eltwise_param()->set_operation(
EltwiseParameter_EltwiseOp_SUM); //"Eltwise"的case是EltwiseParameter_EltwiseOp_SUM和EltwiseParameter_EltwiseOp_MAX

LayerParameter slice_param;
slice_param.set_type("Slice");//caffe里的层作用是将bottom按照某一维需要分解成多个tops
slice_param.mutable_slice_param()->set_axis(0);//为何默认设置成0呢?设成1下面就不需要再设了啊?
//这个版本少了个 scale_param.set_type("Scale");
LayerParameter split_param; //Scale层:y=alpha×x_norm + beta
split_param.set_type("Split");//caffe里的层,将bottom复制多份,输出到tops


BlobShape input_shape;
input_shape.add_dim(1);// c_0 and h_0 are a single timestep //就是shape 多加一维呗
input_shape.add_dim(this->N_);//N_定义在sequence_layers中,视频流24或3
input_shape.add_dim(num_output); //add_dim不在caffe.proto中 多加256维

net_param->add_input("c_0");//加入输入层“c_0”
net_param->add_input_shape()->CopyFrom(input_shape);

net_param->add_input("h_0");//加入输入层“h_0”
net_param->add_input_shape()->CopyFrom(input_shape);

LayerParameter* cont_slice_param = net_param->add_layer();
cont_slice_param->CopyFrom(slice_param);
cont_slice_param->set_name("cont_slice");
cont_slice_param->add_bottom("cont");//把cont分解成16份。按说bottom该是reshape_cm。cont在recurrent_layer.cpp中
cont_slice_param->mutable_slice_param()->set_axis(1);//要分解的维度是1,stream

// Add layer to transform all timesteps of x to the hidden state dimension.
// W_xc_x = W_xc * x + b_c //Out=W*x+b的形式
{
LayerParameter* x_transform_param = net_param->add_layer();
x_transform_param->CopyFrom(biased_hidden_param);//用的是有偏置的hidden_param
x_transform_param->set_name("x_transform");//从src\caffe\test_protobuf.cpp能看出来就是建立一个名为“x_transform”的层
x_transform_param->add_param()->set_name("W_xc");
x_transform_param->add_param()->set_name("b_c");
x_transform_param->add_bottom("x");//在recurrent_layer.cpp中
x_transform_param->add_top("W_xc_x");
}

if (this->static_input_) {
// Add layer to transform x_static to the gate dimension. //这里是个持续的输入,从哪来呢?从lstm结构里来。但是这里需要吗?
// W_xc_x_static = W_xc_static * x_static
LayerParameter* x_static_transform_param = net_param->add_layer();
x_static_transform_param->CopyFrom(hidden_param);//用的是没有偏置的hidden_param
x_static_transform_param->mutable_inner_product_param()->set_axis(1);//这里输出O=W*x,所以用了一个一维的吧。
x_static_transform_param->set_name("W_xc_x_static");
x_static_transform_param->add_param()->set_name("W_xc_static");
x_static_transform_param->add_bottom("x_static");
x_static_transform_param->add_top("W_xc_x_static");

LayerParameter* reshape_param = net_param->add_layer();
reshape_param->set_type("Reshape");//用了reshape层
BlobShape* new_shape =
reshape_param->mutable_reshape_param()->mutable_shape();//new_shape=->mutable_shape,意思是修改shape
new_shape->add_dim(1);// One timestep. //添加1个
new_shape->add_dim(this->N_);//添加N_个。视频流24或3
new_shape->add_dim(
x_static_transform_param->inner_product_param().num_output());//设为(num_output * 4);个把持续输入加到bottom(2)
reshape_param->add_bottom("W_xc_x_static");
reshape_param->add_top("W_xc_x_static");
}

LayerParameter* x_slice_param = net_param->add_layer();
x_slice_param->CopyFrom(slice_param); //axis还是默认的=0,所以切的是t
x_slice_param->add_bottom("W_xc_x");//把"W_xc_x"切片,W_xc_x=Wx+b
x_slice_param->set_name("W_xc_x_slice");//放进"W_xc_x_slice"

LayerParameter output_concat_layer;
output_concat_layer.set_name("h_concat");
output_concat_layer.set_type("Concat");
output_concat_layer.add_top("h");
output_concat_layer.mutable_concat_param()->set_axis(0);//frame?那就是每个视频拼好了再输出。

for (int t =1; t <=this->T_; ++t) {//T_应该是16
string tm1s = this->int_to_str(t -1);//这个有: 创建一个流把(t-1)值传递入流中,tm1s应该是time minte 1 s的意思。就是t-1
string ts = this->int_to_str(t);// t时刻

cont_slice_param->add_top("cont_" + ts);
x_slice_param->add_top("W_xc_x_" + ts);//"W_xc_x_slice"top是("W_xc_x_" + ts)。那就是把"W_xc_x"切成16个"W_xc_x_",所以("W_xc_x_" + ts)是二维。

// Add layers to flush the hidden state when beginning a new
// sequence, as indicated by cont_t.
// h_conted_{t-1} := cont_t * h_{t-1}
//
// Normally, cont_t is binary (i.e., 0 or 1), so:
// h_conted_{t-1} := h_{t-1} if cont_t == 1
// 0 otherwise
{
LayerParameter* cont_h_param = net_param->add_layer();
cont_h_param->CopyFrom(sum_param);
cont_h_param->mutable_eltwise_param()->set_coeff_blob(true);//这里的加法应该是判断语句了。
cont_h_param->set_name("h_conted_" + tm1s);
cont_h_param->add_bottom("h_" + tm1s);//h
cont_h_param->add_bottom("cont_" + ts);//按顺序有:[0,1,1,1,...16个]
cont_h_param->add_top("h_conted_" + tm1s);//h(t-1)
}

// Add layer to compute
// W_hc_h_{t-1} := W_hc * h_conted_{t-1}
{
LayerParameter* w_param = net_param->add_layer();//g(t)第二项,W*h(t-1)
w_param->CopyFrom(hidden_param); //用的是没有偏置的hidden_param
w_param->set_name("transform_" + ts);
w_param->add_param()->set_name("W_hc");
w_param->add_bottom("h_conted_" + tm1s);
w_param->add_top("W_hc_h_" + tm1s);
w_param->mutable_inner_product_param()->set_axis(2);//这里设为2,按说应该含有b,但这里没有b,那么说多的这一维应该是tm1s的?
} //这里牵扯到t时刻,set_axis(2);的第二维应该对应的t。

// Add the outputs of the linear transformations to compute the gate input.
// gate_input_t := W_hc * h_conted_{t-1} + W_xc * x_t + b_c //这里的b_c可能在上面计算"W_hc_h_"时产生的,因为上面set_axis(2)。不是
// = W_hc_h_{t-1} + W_xc_x_t + b_c
{
LayerParameter* input_sum_layer = net_param->add_layer();//g(t)
input_sum_layer->CopyFrom(sum_param);
input_sum_layer->set_name("gate_input_" + ts);
input_sum_layer->add_bottom("W_hc_h_" + tm1s);//假如 ("W_hc_h_" + tm1s);在上面是二维的,其他俩应该也是才对?
input_sum_layer->add_bottom("W_xc_x_" + ts);//应该是size([W*x + b])*size(t)大小的矩阵形式,这个是上面定义的二维的。
if (this->static_input_) {
input_sum_layer->add_bottom("W_xc_x_static");//有没有一个持续的输入,有的话再加个"W_xc_x_static"的bottom
}
input_sum_layer->add_top("gate_input_" + ts);
}

// Add LSTMUnit layer to compute the cell & hidden vectors c_t and h_t.
// Inputs: c_{t-1}, gate_input_t = (i_t, f_t, o_t, g_t), cont_t
// Outputs: c_t, h_t
// [ i_t' ]
// [ f_t' ] := gate_input_t
// [ o_t' ]
// [ g_t' ]
// i_t := \sigmoid[i_t']
// f_t := \sigmoid[f_t']
// o_t := \sigmoid[o_t']
// g_t := \tanh[g_t']
// c_t := cont_t * (f_t .* c_{t-1}) + (i_t .* g_t)
// h_t := o_t .* \tanh[c_t]
{
LayerParameter* lstm_unit_param = net_param->add_layer();
lstm_unit_param->set_type("LSTMUnit");//在REGISTER_LAYER_CLASS里注册的lstmunitlayer就叫这个。此处应该是使用了LSTMUnit层。
lstm_unit_param->add_bottom("c_" + tm1s);//C(t-1)细胞状态 对应lstmunit里的bottom[0] //就是一个层的输入,top是输出。add_bottom("c_" + tm1s),这里的意思是:。
lstm_unit_param->add_bottom("gate_input_" + ts);//对应lstmunit里的bottom[1],是X //C_1,C_2,C_3......分别代表每一个时刻的clip marker。假如是第2时刻,这个时候输入应有3个(data_2,label_2,C_1),
lstm_unit_param->add_bottom("cont_" + ts);//对应lstmunit里的bottom[2],是flush //下标表示时刻,C要注意,是看上一时刻来判断下面一帧还想不想连。add_bottom("c_" + tm1s)这句话只是输入tm1s时刻的C
lstm_unit_param->add_top("c_" + ts);
lstm_unit_param->add_top("h_" + ts);
lstm_unit_param->set_name("unit_" + ts);
}
output_concat_layer.add_bottom("h_" + ts);//ts = t,即ts = this->int_to_str(t)。这个也是最上面recurrentoutput设定的输出。
} // for (int t = 1; t <= this->T_; ++t)。 这是for (int t = 1; t <= this->T_; ++t)循环的结尾。
//循环过后output_concat_layer应该是多个ts时刻的“h_”,这个bottom的blob应该是T_维“h_”。
{
LayerParameter* c_T_copy_param = net_param->add_layer();
c_T_copy_param->CopyFrom(split_param);
c_T_copy_param->add_bottom("c_" +this->int_to_str(this->T_));//复制c_到c_T
c_T_copy_param->add_top("c_T");//这个c_T是最上面recurrentoutput设定的输出。
}
net_param->add_layer()->CopyFrom(output_concat_layer);//16个h_?还是一个h_
}

INSTANTIATE_CLASS(LSTMLayer);
REGISTER_LAYER_CLASS(LSTM);

} // namespace caffe