【caffe-Windows】關於LSTM的簡單小例子
前言
這裡主要是看到了一個簡單的LSTM例子,比上一個coco簡單很多,所以在這裡記錄一下,便於後續的分析,參考部落格在上一篇文章的末尾提到過:Recurrent neural nets with Caffe
需要說明的是這個例子也並非原原本本的使用caffe自帶的LSTM,而是加入了一些東西,所以我們還是得新增層
國際慣例,程式碼連結:
博主提供雲盤:連結:http://pan.baidu.com/s/1i474Dd7 密碼:31x8
第一步
首先對比一下兩個proto,看看我們的caffe-Windows少了哪幾個層。一般而言,操作多了就會發現,我們的第一反應去看看message LayerParameter這裡面登記的ID,可以發現作者添加了以下幾層:
①LSTM層:對應lstm_layer.hpp和lstm_layer.cpp注意這個地方由於上一篇部落格配置過LSTM,而且此處的LSTM與上一篇部落格實現不同,所以最好是重新解壓一個caffe-Windows執行新增層的操作
新增ID為
optional LSTMParameter lstm_param = 201;
新增引數為
message LSTMParameter { optional uint32 num_output = 1; // The number of outputs for the layer optional float clipping_threshold = 2 [default = 0.0]; optional FillerParameter weight_filler = 3; // The filler for weight optional FillerParameter bias_filler = 4; // The filler for the bias optional uint32 batch_size = 5 [default = 1]; }
②Repeat層:對應repeat_layer.hpp和repeat_layer.cpp
新增ID為
optional RepeatParameter repeat_param = 202;
新增引數為
message RepeatParameter {
optional uint32 num_repeats = 1; // The number of outputs for the layer
optional int32 axis = 2 [default = 1];
}
③PowerFile層:對應power_file_layer.hpp和power_file_layer.cpp
新增ID為
optional PowerFileParameter power_file_param = 146;
新增引數為
// added by Kaichun Mo
message PowerFileParameter {
optional string shift_file = 1;
}
④loc_loss層:對應loc_loss_layer.hpp和loc_loss_layer.cpp
新增ID為
optional LocLossParameter loc_loss_param = 147;
新增引數為
message LocLossParameter {
required double threshold = 1;
}
⑤SpatialTransformer層:對應st_layer.hpp和st_layer.cpp
新增ID為
optional SpatialTransformerParameter st_param = 148;
新增引數為⑥STLoss層:對應st_loss_layer.hpp和st_loss_layer.cpp
新增ID為
optional STLossParameter st_loss_param = 145;
新增內容為
message STLossParameter {
// Indicate the resolution of the output images after ST transformation
required int32 output_H = 1;
required int32 output_W = 2;
}
⑦smooth_L1_loss層:對應smooth_L1_loss_layer.hpp和smooth_L1_loss_layer.cpp
無ID無引數
這一步的主要問題在於:
①新拷貝一個caffe-Windows再新增層,因為我第一次是將lstm_layer.hpp和lstm_layer.cpp都換了個名字,但是一直編譯不成功出錯。有C++大牛的話可以嘗試一下,也算是一種挑戰。
②新增層的方法沒有寫詳細,強烈建議大家一層一層新增,否則很容易出錯,而且你不知道錯誤原因何在。而且每一層新增完成測試的方法在上上篇部落格有說明,就是單獨對這個cpp編譯會顯示成功。
多句嘴:仔細、仔細、仔細
第二步
很多人在使用matlab介面的時候會說:“哎呀,咋有崩潰了啊”,“哎呀,又閃退了”,“又未響應了”。這種情況我的解決方法是使用exe除錯,當然每個人有每個人的除錯方法,都靠自己探索的。
所以第二步,先不在MATLAB中進行使用,而是測試一下我們書寫的LSTM的prototxt檔案是否正確,新手一般都會出現什麼parameterfailed什麼的錯誤,反正大概意思就是不存在這個層。那麼你就得回到第一步再檢查一遍了,提示的是誰,就檢查誰
廢話說多了,下面開始測試。使用的模型來源於參考部落格,先看看網路結構
看著挺簡單的,先介紹一下輸入(從原始部落格翻譯過來):
data:是T*N*I維度,其中I代表序列每一步中資料的維度(例子中為1),T*N代表序列長度,其中N是batchsize批大小。約束是資料大小必須是N的整數倍
clip:是T*N維度,如果clip=0,隱狀態和記憶單元就會被初始化為0,如果不是的話,之前的值將會被使用
label:每一步的預測輸出
根據模型搭建網路(lstm.prototxt)如下:
name: "LSTM"
input: "data"
input_shape { dim: 320 dim: 1 }
input: "clip"
input_shape { dim: 320 dim: 1 }
input: "label"
input_shape { dim: 320 dim: 1 }
layer {
name: "Silence"
type: "Silence"
bottom: "label"
include: { phase: TEST }
}
layer {
name: "lstm1"
type: "Lstm"
bottom: "data"
bottom: "clip"
top: "lstm1"
lstm_param {
num_output: 15
clipping_threshold: 0.1
weight_filler {
type: "gaussian"
std: 0.1
}
bias_filler {
type: "constant"%偏置初始化為0
}
}
}
layer {
name: "ip1"
type: "InnerProduct"
bottom: "lstm1"
top: "ip1"
inner_product_param {
num_output: 1
weight_filler {
type: "gaussian"
std: 0.1
}
bias_filler {
type: "constant"
}
}
}
layer {
name: "loss"
type: "EuclideanLoss"
bottom: "ip1"
bottom: "label"
top: "loss"
include: { phase: TRAIN }
}
有了網路模型,需要配置一個solver.prototxt去呼叫它
net: "lstm.prototxt"
test_iter: 1
test_interval: 2000000
base_lr: 0.0001
momentum: 0.95
lr_policy: "fixed"
display: 200
max_iter: 100000
solver_mode: CPU
average_loss: 200
#debug_info: true
接下來建立一個test.bat用於測試我們的caffe是否搭建好,是否適用於此prototxt,其中test.bat的內容如下
E:\CaffeDev2\caffe-windows\Build\x64\Release\caffe.exe train -solver=solver.prototxt
pause
看看我的目錄結構,主要還是為了強調路徑問題
如果這一步出錯,請重新操作第一步
第三步
既然模型沒問題了,那麼就可以新增資料進行預測了。採用參考博文中的輸入函式進行測試。
在此之前,參考前面的配置MATLAB介面的博文,確保自帶的classification.m能夠正常執行。
之後我們來測試一下模型預測效果:
①新增環境變數
clear
clc
addpath('../..')
caffe.reset_all
caffe.set_mode_cpu
②載入模型
solver=caffe.Solver('solver.prototxt');
執行看看,崩潰了請重新核對路徑以及MATLAB介面的配置還有第二步是否能成功
>> solver
solver =
Solver (具有屬性):
net: [1x1 caffe.Net]
test_nets: [1x1 caffe.Net]
設定遺忘門的偏置為5.0
③建立資料集
a=0:0.01:32;
a=a(1:end-1);
d=0.5 * sin(2*a) - 0.05 * cos( 17*a + 0.8 ) + 0.05 * sin( 25 * a + 10 ) - 0.02 * cos( 45 * a + 0.3);
d = d / max(max(d), -min(d));
d = d - mean(d);
④訓練
niter=5000;
solver.net.blobs('clip').set_data(solver.net.blobs('clip').get_data*0+1);
train_loss =zeros(1,niter);
for i=1:niter
if mod(i,1000)==0
fprintf('iter=%d\n',i);
end
seq_idx = mod(i ,(length(d) / 320));
mid=solver.net.blobs('clip').get_data();
solver.net.blobs('clip').set_data([seq_idx > 0,mid(2:end)]);
solver.net.blobs('label').set_data(d( seq_idx * 320+1 : (seq_idx+1) * 320 ));
solver.step(1)
train_loss(i)= solver.net.blobs('loss').get_data();
end
畫出來看看
plot(1:niter,train_loss)
再測試一下
preds=zeros(length(d));
solver.test_nets.blobs('clip').set_data(solver.test_nets.blobs('clip').get_data*0+1)
for i=1:length(d)
mid2=solver.test_nets.blobs('clip').get_data();
solver.test_nets.blobs('clip').set_data([i>0,mid2(2:end)]);
solver.test_nets.forward_prefilled()
mid2=solver.test_nets.blobs('ip1').get_data();
preds(i)=mid2(1);
end
figure
plot(preds)
hold on
plot(d)
hold off
第四步
感覺和參考博文中的結果差距很大啊,去Python中測試一下
import caffe
solver = caffe.SGDSolver('solver.prototxt')
solver.net.params['lstm1'][2].data[15:30]=5
import numpy as np
a = np.arange(0,32,0.01)
d = 0.5*np.sin(2*a) - 0.05 * np.cos( 17*a + 0.8 ) + 0.05 * np.sin( 25 * a + 10 ) - 0.02 * np.cos( 45 * a + 0.3)
d = d / max(np.max(d), -np.min(d))
d = d - np.mean(d)
niter=5000
train_loss = np.zeros(niter)
solver.net.params['lstm1'][2].data[15:30]=5
solver.net.blobs['clip'].data[...] = 1
for i in range(niter) :
seq_idx = i % (len(d) / 320)
solver.net.blobs['clip'].data[0] = seq_idx > 0
solver.net.blobs['label'].data[:,0] = d[ seq_idx * 320 : (seq_idx+1) * 320 ]
solver.step(1)
train_loss[i] = solver.net.blobs['loss'].data
import matplotlib.pyplot as plt
plt.plot(np.arange(niter), train_loss)
solver.test_nets[0].blobs['data'].reshape(2,1)
solver.test_nets[0].blobs['clip'].reshape(2,1)
solver.test_nets[0].reshape()
solver.test_nets[0].blobs['clip'].data[...] = 1
preds = np.zeros(len(d))
for i in range(len(d)):
solver.test_nets[0].blobs['clip'].data[0] = i > 0
preds[i] = solver.test_nets[0].forward()['ip1'][0][0]
plt.plot(np.arange(len(d)), preds)
plt.plot(np.arange(len(d)), d)
plt.show()
然而也無法復現原文效果,有對Python比較瞭解的大牛希望能夠在評論區給出相關測試意見。
難道是作者博文寫錯了?這個以後再探討。
總結:
通過這兩個關於LSTM的部落格發現,不同的人對LSTM的改編都有所不同,根據不同的任務,可以對LSTM做相應的改動,使用方法也不盡相同。
但是語法結構應該是相似的,對比一下兩篇博文中關於LSTM層的寫法可能就會有不同的收穫