Caffe LSTM 实现mnist识别

基础理解

 

假如 Caffe训练网络的网络结果如下所示: 

相关术语及变量维度关系

  • N为LSTM同时处理的独立流的个数(Batch size),在该实验中为输入LSTM相互独立的视频的个数,以该实验测试网络为例,本文取T=3。

  • T为LSTM网络层处理的时间步总数(LSTM的时间步,即循环次数),在该实验中为输入LSTM的任意一独立视频的视频帧个数,以该实验测试网络为例,本文取T=16

  • 因此fc-reshape层输出维度为 TxNx4096   4096为AlexNet中全连接层的维度,即CNN特征的维度。

  • reshape-cm的输出维度为 TxN,即每一个帧有一个是否连续帧的标志。(第一帧为0,后面的连续帧为1)

  • reshape-label的维度同样为 TxN

name: "LeNet"
layer {
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TRAIN
  }
  transform_param {
    scale: 0.00390625
  }
  data_param {
    source: "lmdb/mnist_train_lmdb"
    batch_size: 1
    backend: LMDB
  }
}
layer {
  name: "mnist"
  type: "Data"
  top: "data"
  top: "label"
  include {
    phase: TEST
  }
  transform_param {
    scale: 0.00390625
  }
  data_param {
    source: "lmdb/mnist_test_lmdb"
    batch_size: 1
    backend: LMDB
  }
}
# 将 data 转换为 [T,Batch, H]
layer {
  name: "reshape"
  type: "Reshape"
  bottom: "data"
  top: "reshape1"
  reshape_param {
    shape { dim:28 dim:1 dim:28}
  }
}
layer {
  name: "cont"
  type: "HDF5Data"
  top: "cont"
  include {
    phase: TRAIN
  }
  hdf5_data_param {
    source: "train_conv_h5.txt"
    batch_size: 1
  }
}
layer {
  name: "cont"
  type: "HDF5Data"
  top: "cont"
  include {
    phase: TEST
  }
  hdf5_data_param {
    source: "test_conv_h5.txt"
    batch_size: 1
  }
}
#将 cont 转换为 [T, batch]
layer {
  name: "reshape"
  type: "Reshape"
  bottom: "cont"
  top: "cont1"
  reshape_param {
    shape { dim:28 dim:1}
  }
}
#num_output 代表每个 lstm的输出 为 10 个数值 ,只使用第28个时间步的值 
layer {
  name: "lstm1"
  type: "LSTM"
  bottom: "reshape1"
  bottom: "cont1"
  top: "lstm1"
  recurrent_param {
    num_output: 10
    weight_filler {
      type: "uniform"
      min: -0.08
      max: 0.08
    }
    bias_filler {
      type: "constant"
      value: 0
    }
  }
}
#输出为28个时间步的 output [t, batch, 10] 只使用第28个时间步的值 output2
layer {
  name: "slice"
  type: "Slice"
  bottom: "lstm1"
  top: "output1"
  top: "output2"
  slice_param {
    axis: 0
    slice_point: 27
  }
 }
 #output1 一直输出影响观察,这里随便加的一层,没有作用
  layer {
  name: "reduction"
  type: "Reduction"
  bottom: "output1"
  top: "output11"
  reduction_param {
		axis: 0
  }
}

 layer {
  name: "reshape"
  type: "Reshape"
  bottom: "output2"
  top: "output22"
  reshape_param {
    shape { dim:1, dim:-1}
  }
}

layer {
  name: "accuracy"
  type: "Accuracy"
  bottom: "output22"
  bottom: "label"
  top: "accuracy"
  include {
    phase: TEST
  }
}
layer {
  name: "loss"
  type: "SoftmaxWithLoss"
  bottom: "output22"
  bottom: "label"
  top: "loss"
}

 

 

cont 数据生成  (用于表示 帧是否连续,一般只有第一帧是不连续的,后续都为连续) , 代码为 h5文件的生成方式

Mat data_train1(64, 28, CV_8UC1), data_test1(64, 28, CV_8UC1);
	for (size_t i = 0; i < 64; i++)
	{
		for (size_t j = 0; j < 28; j++)
		{
			if (j == 0)
			{
				data_train1.at<uchar>(i, j) = 0;
				data_test1.at<uchar>(i, j) = 0;
			}
			else {
				data_train1.at<uchar>(i, j) = 1;
				data_test1.at<uchar>(i, j) = 1;
			}
			
		}
	}
	mat2hdf5(data_train1, "D:\\MNIST\\data\\train_cont.h5", "cont");
	mat2hdf5(data_test1, "D:\\MNIST\\data\\test_cont.h5", "cont");
	system("PAUSE");
	for (size_t i = 0; i < 64; i++)
	{
		for (size_t j = 0; j < 28; j++)
		{
			if (j == 0)
			{
				data_train1.at<uchar>(i, j) = 0;
				data_test1.at<uchar>(i, j) = 0;
			}
			else {
				data_train1.at<uchar>(i, j) = 1;
				data_test1.at<uchar>(i, j) = 1;
			}
			
		}
	}
	mat2hdf5(data_train1, "D:\\MNIST\\data\\train_cont.h5", "cont");
	mat2hdf5(data_test1, "D:\\MNIST\\data\\test_cont.h5", "cont");
	system("PAUSE");
void mat2hdf5(Mat &data, const char * filepath, string dataset1)
{
	int data_cols = data.cols;
	int data_rows = data.rows;


	hid_t file_id;
	herr_t status;
	file_id = H5Fcreate(filepath, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
	int rank_data = 2;
	hsize_t dims_data[2];
	dims_data[0] = data_rows;
	dims_data[1] = data_cols;
	hid_t data_id = H5Screate_simple(rank_data, dims_data, NULL);


	hid_t dataset_id = H5Dcreate2(file_id, dataset1.c_str(), H5T_NATIVE_FLOAT, data_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
	int i, j;
	float* data_mem = new float[data_rows*data_cols];
	float **array_data = new float*[data_rows];
	for (j = 0; j < data_rows; j++) {
		array_data[j] = data_mem + j* data_cols;
		for (i = 0; i < data_cols; i++)
		{
			array_data[j][i] = data.at<uchar>(j, i);
		}
	}
	status = H5Dwrite(dataset_id, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, array_data[0]);
	//关闭
	status = H5Sclose(data_id);
	status = H5Dclose(dataset_id);
	status = H5Fclose(file_id);
	delete[] array_data;
}
	int data_cols = data.cols;
	int data_rows = data.rows;


	hid_t file_id;
	herr_t status;
	file_id = H5Fcreate(filepath, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
	int rank_data = 2;
	hsize_t dims_data[2];
	dims_data[0] = data_rows;
	dims_data[1] = data_cols;
	hid_t data_id = H5Screate_simple(rank_data, dims_data, NULL);


	hid_t dataset_id = H5Dcreate2(file_id, dataset1.c_str(), H5T_NATIVE_FLOAT, data_id, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT);
	int i, j;
	float* data_mem = new float[data_rows*data_cols];
	float **array_data = new float*[data_rows];
	for (j = 0; j < data_rows; j++) {
		array_data[j] = data_mem + j* data_cols;
		for (i = 0; i < data_cols; i++)
		{
			array_data[j][i] = data.at<uchar>(j, i);
		}
	}
	status = H5Dwrite(dataset_id, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, array_data[0]);
	//关闭
	status = H5Sclose(data_id);
	status = H5Dclose(dataset_id);
	status = H5Fclose(file_id);
	delete[] array_data;
}

生成 的 h5 内容 如图所示:

test_cont 与 train_cont 数据一样,64*28,其中64 是可变的。其实只要由一条就够了,取batch 个 应该会循环取这个数据。

 

由于 输入数据 要进行 维度转换  由[batch, 1, w, h] 这里把 w 看成时间部 T

应该转换成 [t, batch, h]  ,在caffe 里面没有找到维度变换的方法,所以直接使用的reshape ,为了保证有效,batch 必须设置成1,否者与实际转换不符。

其他注释 在 prototxt 文件里了。

 

训练结果  准确率并不高, 主要还是理解 lstm 这个 过程

I0715 17:11:19.591768 10532 solver.cpp:348] Iteration 790000, Testing net (#0)
I0715 17:11:19.600821 10532 solver.cpp:415]     Test net output #0: accuracy = 0.83
I0715 17:11:19.600821 10532 solver.cpp:415]     Test net output #1: loss = 1.18451 (* 1 = 1.18451 loss)
I0715 17:11:19.600821 10532 solver.cpp:415]     Test net output #2: output11 = -35.0393
I0715 17:11:19.600821 10532 solver.cpp:220] Iteration 790000 (6993.01 iter/s, 0.715s/5000 iters), loss = 1.00843
I0715 17:11:19.600821 10532 solver.cpp:239]     Train net output #0: loss = 1.00847 (* 1 = 1.00847 loss)
I0715 17:11:19.600821 10532 solver.cpp:239]     Train net output #1: output11 = -43.1359
I0715 17:11:19.600821 10532 sgd_solver.cpp:105] Iteration 790000, lr = 0.000373837
I0715 17:11:20.325745 10532 solver.cpp:348] Iteration 795000, Testing net (#0)
I0715 17:11:20.335746 10532 solver.cpp:415]     Test net output #0: accuracy = 0.77
I0715 17:11:20.335746 10532 solver.cpp:415]     Test net output #1: loss = 1.24829 (* 1 = 1.24829 loss)
I0715 17:11:20.335746 10532 solver.cpp:415]     Test net output #2: output11 = -37.8989
I0715 17:11:20.335746 10532 solver.cpp:220] Iteration 795000 (6811.99 iter/s, 0.734s/5000 iters), loss = 0.868423
I0715 17:11:20.335746 10532 solver.cpp:239]     Train net output #0: loss = 0.868455 (* 1 = 0.868455 loss)
I0715 17:11:20.335746 10532 solver.cpp:239]     Train net output #1: output11 = -54.416
I0715 17:11:20.335746 10532 sgd_solver.cpp:105] Iteration 795000, lr = 0.000372094
I0715 17:11:21.056661 10532 solver.cpp:468] Snapshotting to binary proto file save_path/_iter_800000.caffemodel
I0715 17:11:21.058668 10532 sgd_solver.cpp:273] Snapshotting solver state to binary proto file save_path/_iter_800000.solverstate
I0715 17:11:21.059669 10532 solver.cpp:328] Iteration 800000, loss = 1.01535
I0715 17:11:21.059669 10532 solver.cpp:348] Iteration 800000, Testing net (#0)
I0715 17:11:21.068692 10532 solver.cpp:415]     Test net output #0: accuracy = 0.75
I0715 17:11:21.068692 10532 solver.cpp:415]     Test net output #1: loss = 1.32086 (* 1 = 1.32086 loss)
I0715 17:11:21.068692 10532 solver.cpp:415]     Test net output #2: output11 = -33.7201
I0715 17:11:21.068692 10532 solver.cpp:333] Optimization Done.
I0715 17:11:21.068692 10532 caffe.cpp:260] Optimization Done.

 

 

更多理解
 

在 caffe 上配置 LSTM 时,数据的维度比较复杂。比如在 CNN 处理图片时,caffe 的数据维度一般是 N*C*H*W,N 是 batch size,C 是 channel,W 是 width,H 是 height;但是 LSTM 的数据维度是 T*N*Vector,T 是 sequence length,N 是 batch size。很多人就会搞混这里的 batch size 和 CNN 下的 batch size,如果是一个 CNN+LSTM 网络,到底 data layer 的 batch size 按照哪个配置?

我是理解是这样:比如你要处理视频的连续帧,数据是这样的:data 数据集是每帧的图片 raw data(lenx3x320x320),label 数据集是每帧对应的 label (lenx1),cont 代表这帧是否为连续帧,0代表头帧,1代表连续帧 (lenx1)。

data layer 的 batch_size 其实是 T*N,因为只有1个流,所以这里 N 是 1。如果 data layer 的 batch_size 设置成 64,T 就是 64,N 就是1,照常进行后续 convolution layer 的配置。

layer {
  name: "data"
  type: "HDF5Data"
  top: "data"
  top: "label"
  top: "cont"
  hdf5_data_param {
    source: "train_hdf5.txt"
    batch_size: 64
  }
}

然后 convolution layer 后面接全连接层,然后接 LSTM layer 时,比如全连接层的 output 是 10,那这层输出的数据就是 64*10。如果想在这时候加个 LSTM layer,因为 cont 是 64*1 的,就需要加个 reshape 层。

layer {
  name: "reshape"
  type: "Reshape"
  bottom: "fc1"
  top: "reshape1"
  reshape_param {
    shape { dim: 0 dim: 1 dim: -1 }
  }
}

就是把原来 64*10 的数据,reshape 成 64*1*10,这样才能匹配上输入的 cont 64*1。

layer {
  name: "lstm"
  type: "LSTM"
  bottom: "reshape1"
  bottom: "cont"
  top: "output"
  recurrent_param {
    num_output: 1
  }
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
Caffe是一个深度学习框架,它主要用于卷积神经网络。然而,由于LSTM网络也是深度学习的一种形式,因此Caffe也可以用于实现LSTM网络。 在Caffe中,实现LSTM网络的步骤与实现卷积神经网络的步骤类似。主要的区别在于LSTM网络中的神经元类型和连接方式。 以下是一个简单的LSTM网络的示例代码: ```python # 定义LSTM网络结构 net = caffe.NetSpec() net.data = caffe.layers.Input(shape=dict(dim=[1, 1, 10, 1])) # 定义LSTM层 net.lstm1 = caffe.layers.LSTM(num_output=10, weight_filler=dict(type='xavier')) net.lstm2 = caffe.layers.LSTM(num_output=10, weight_filler=dict(type='xavier')) # 定义全连接层 net.fc = caffe.layers.InnerProduct(num_output=1, weight_filler=dict(type='xavier')) # 定义网络结构 net.lstm1_top, net.lstm1_memory = net.lstm1(net.data, None) net.lstm2_top, net.lstm2_memory = net.lstm2(net.lstm1_top, net.lstm1_memory) net.fc_top = net.fc(net.lstm2_top) # 生成网络 with open('lstm.prototxt', 'w') as f: f.write(str(net.to_proto())) ``` 在这个示例中,我们定义了一个包含两个LSTM层和一个全连接层的网络。我们使用`LSTM`层来定义LSTM神经元,并使用`InnerProduct`层定义全连接层。我们还使用`Input`层来定义输入数据的形状。 在定义完网络结构之后,我们可以使用`to_proto()`方法将网络结构以字符串的形式写入文件中。这个文件可以被Caffe加载并用于训练和测试LSTM网络。 需要注意的是,与卷积神经网络不同,LSTM网络需要定义内部记忆状态。在这个示例中,我们使用`net.lstm1_memory`和`net.lstm2_memory`来存储LSTM层的内部状态,以便在下一次前向传递中使用。 此外,还需要注意LSTM网络的训练过程中需要使用BPTT(Back-Propagation Through Time)算法。这个算法是用于处理时间序列数据的反向传播算法。在Caffe中,我们可以使用`LSTMUnitLayer`层来实现BPTT算法。 总之,Caffe可以用于实现LSTM网络,只需要将LSTM层和全连接层添加到网络中,并定义好内部状态和BPTT算法即可。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NineDays66

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值