2016-04-25 105 views
-1

我正在學習LSTM網絡,並決定嘗試綜合測試。我想通過一些點(X,Y)供給LSTM網絡三個基本功能之間進行區分:如何訓練最簡單的功能識別LSTM

  • 線爲:y = K * X + B
  • 拋物線:Y = K * X^2 + B
  • SQRT:Y = K * SQRT(X)+ b

我使用LUA +炬。

數據集是完全虛擬的 - 它是在「數據集」對象上實時創建的。當訓練週期要求樣本的另一個小樣本時,函數mt .__ index會返回動態創建的樣本。它隨機選擇三個所描述的功能併爲它們挑選一些隨機點。

想法是,LSTM網絡將學習一些功能,以識別最後點屬於哪種功能。

完整而簡單的源腳本包括:

require "torch" 
require "nn" 
require "rnn" 

-- hyper-parameters 
batchSize = 8 
rho = 5 -- sequence length 
hiddenSize = 100 
outputSize = 3 
lr = 0.001 

-- Initialize synthetic dataset 
-- dataset[index] returns table of the form: {inputs, targets} 
-- where inputs is a set of points (x,y) of a randomly selected function: line, parabola, sqrt 
-- and targets is a set of corresponding class of a function (1=line, 2=parabola, 3=sqrt) 
local dataset = {} 
dataset.size = function (self) 
    return 1000 
end 
local mt = {} 
mt.__index = function (self, i) 
    local class = math.random(3) 

    local t = torch.Tensor(3):zero() 
    t[class] = 1 
    local targets = {} 
    for i = 1,batchSize do table.insert(targets, class) end 

    local inputs = {} 
    local k = math.random() 
    local b = math.random()*5 

    -- Line 
    if class == 1 then 
    for i = 1,batchSize do 
     local x = math.random()*10 + 5 
     local y = k*x + b 
     input = torch.Tensor(2) 
     input[1] = x 
     input[2] = y 
     table.insert(inputs, input) 
    end 

    -- Parabola 
    elseif class == 2 then 
    for i = 1,batchSize do 
     local x = math.random()*10 + 5 
     local y = k*x*x + b 
     input = torch.Tensor(2) 
     input[1] = x 
     input[2] = y 
     table.insert(inputs, input) 
    end 

    -- Sqrt 
    else 
    for i = 1,batchSize do 
     local x = math.random()*5 + 5 
     local y = k*math.sqrt(x) + b 
     input = torch.Tensor(2) 
     input[1] = x 
     input[2] = y 
     table.insert(inputs, input) 
    end 
    end 

    return { inputs, targets } 
end -- dataset.__index meta function 
setmetatable(dataset, mt) 

-- Initialize random number generator 
math.randomseed(os.time()) 

-- build simple recurrent neural network 
local model = nn.Sequencer(
    nn.Sequential() 
    :add(nn.LSTM(2, hiddenSize, rho)) 
    :add(nn.Linear(hiddenSize, outputSize)) 
    :add(nn.LogSoftMax()) 
) 

print(model) 

-- build criterion 
local criterion = nn.SequencerCriterion(nn.ClassNLLCriterion()) 

-- training 
model:training() 

local epoch = 1 
while true do 

    print ("Epoch "..tostring(epoch).." started") 

    for iteration = 1, dataset:size() do 
    -- 1. Load minibatch of samples 
    local sample = dataset[iteration] -- pick random sample (dataset always returns random set) 
    local inputs = sample[1] 
    local targets = sample[2] 

    -- 2. Perform forward run and calculate error 
    local outputs = model:forward(inputs) 
    local err = criterion:forward(outputs, targets) 

    print(string.format("Epoch %d Iteration %d Error = %f", epoch, iteration, err)) 

    -- 3. Backward sequence through model(i.e. backprop through time) 
    local gradOutputs = criterion:backward(outputs, targets) 
    -- Sequencer handles the backwardThroughTime internally 
    model:backward(inputs, gradOutputs) 
    model:updateParameters(lr) 
    model:zeroGradParameters()  

    end -- for dataset 

    epoch = epoch + 1 
end -- while epoch 

的問題是:網絡不收斂。 你能分享我做錯什麼嗎?

回答

0

我決定發佈我自己的答案,因爲我解決了問題並收到了良好的結果。

首先介紹LSTM在這類任務中的適用性。如前所述,LSTM處理時間序列是很好的。你也可以將線,拋物線和sqrt看作一種時間函數。所以LSTM在這裏完全適用。假設你正在接受實驗結果,那麼一個向量,你想知道什麼樣的函數可以描述你的系列?

有人可能會爭辯說,在上面的代碼中,我們總是得到具有固定點數的進給NN(即batch_size)。那麼爲什麼要使用LSTM?也許嘗試使用一些線性或卷積網絡?

那麼,別忘了 - 這是一個綜合測試。在一個真實的應用程序中,您可能會向NN提供大量的數據點,並期望它能夠識別函數的形式。

例如,在下面我們列車 NN的代碼與8點一次(的batch_size),但是當我們測試 NN我們只使用4點(test_size)。

而且我們得到了相當不錯的結果:大約1000次迭代後,NN給出了〜99%的正確答案。

但是單層神經網絡不是魔術師。如果我們在每次迭代中更改函數的形式,它就無法學習任何功能。即在原始代碼kb被更改爲每個請求數據集。我們應該做的是在啓動時生成它們,不要改變。

所以下面的工作代碼:

require "torch" 
require "nn" 
require "rnn" 

-- Initialize random number generator 
math.randomseed(os.time()) 

-- hyper-parameters 
batch_size = 8 
test_size = 4 
rho = 5 -- sequence length 
hidden_size = 100 
output_size = 3 
learning_rate = 0.001 

-- Initialize synthetic dataset 
-- dataset[index] returns table of the form: {inputs, targets} 
-- where inputs is a set of points (x,y) of a randomly selected function: line, parabola, sqrt 
-- and targets is a set of corresponding class of a function (1=line, 2=parabola, 3=sqrt) 
local dataset = {} 
dataset.k = math.random() 
dataset.b = math.random()*5 
dataset.size = function (self) 
    return 1000 
end 
local mt = {} 
mt.__index = function (self, i) 
    local class = math.random(3) 

    local t = torch.Tensor(3):zero() 
    t[class] = 1 
    local targets = {} 
    for i = 1,batch_size do table.insert(targets, class) end 

    local inputs = {} 
    local k = self.k 
    local b = self.b 

    -- Line 
    if class == 1 then 
    for i = 1,batch_size do 
     local x = math.random()*10 + 5 
     local y = k*x + b 
     input = torch.Tensor(2) 
     input[1] = x 
     input[2] = y 
     table.insert(inputs, input) 
    end 

    -- Parabola 
    elseif class == 2 then 
    for i = 1,batch_size do 
     local x = math.random()*10 + 5 
     local y = k*x*x + b 
     input = torch.Tensor(2) 
     input[1] = x 
     input[2] = y 
     table.insert(inputs, input) 
    end 

    -- Sqrt 
    else 
    for i = 1,batch_size do 
     local x = math.random()*5 + 5 
     local y = k*math.sqrt(x) + b 
     input = torch.Tensor(2) 
     input[1] = x 
     input[2] = y 
     table.insert(inputs, input) 
    end 
    end 

    return { inputs, targets } 
end -- dataset.__index meta function 
setmetatable(dataset, mt) 


-- build simple recurrent neural network 
local model = nn.Sequencer(
    nn.Sequential() 
    :add(nn.LSTM(2, hidden_size, rho)) 
    :add(nn.Linear(hidden_size, output_size)) 
    :add(nn.LogSoftMax()) 
) 

print(model) 

-- build criterion 
local criterion = nn.SequencerCriterion(nn.ClassNLLCriterion()) 


local epoch = 1 
local err = 0 
local pos = 0 
local N = math.floor(dataset:size() * 0.1) 

while true do 

    print ("Epoch "..tostring(epoch).." started") 

    -- training 
    model:training() 
    for iteration = 1, dataset:size() do 
    -- 1. Load minibatch of samples 
    local sample = dataset[iteration] -- pick random sample (dataset always returns random set) 
    local inputs = sample[1] 
    local targets = sample[2] 

    -- 2. Perform forward run and calculate error 
    local outputs = model:forward(inputs) 
    local _err = criterion:forward(outputs, targets) 

    print(string.format("Epoch %d (pos=%f) Iteration %d Error = %f", epoch, pos, iteration, _err)) 

    -- 3. Backward sequence through model(i.e. backprop through time) 
    local gradOutputs = criterion:backward(outputs, targets) 
    -- Sequencer handles the backwardThroughTime internally 
    model:backward(inputs, gradOutputs) 
    model:updateParameters(learning_rate) 
    model:zeroGradParameters()  

    end -- for training 

    -- Testing 
    model:evaluate() 
    err = 0 
    pos = 0 
    for iteration = 1, N do 
    -- 1. Load minibatch of samples 
    local sample = dataset[ math.random(dataset:size()) ] 
    local inputs = sample[1] 
    local targets = sample[2] 
    -- Drop last points to reduce to test_size 
    for i = #inputs, test_size, -1 do 
     inputs[i] = nil 
     targets[i] = nil 
    end 

    -- 2. Perform forward run and calculate error 
    local outputs = model:forward(inputs) 
    err = err + criterion:forward(outputs, targets) 

    local p = 0 
    for i = 1, #outputs do 
     local _, oi = torch.max(outputs[i], 1) 
     if oi[1] == targets[i] then p = p + 1 end 
    end 
    pos = pos + p/#outputs 

    end -- for testing 
    err = err/N 
    pos = pos/N 
    print(string.format("Epoch %d testing results: pos=%f err=%f", epoch, pos, err)) 

    if (pos > 0.95) then break end 

    epoch = epoch + 1 
end -- while epoch 
1

這種方法是完全錯誤的。以這種方式學習LSTM不會因爲許多原因而學習你想要的東西。我會說出其中的兩個:

  1. 讓我們假設你從(-1, 1)繪製您x均勻。然後功能|x|0.5x + 0.5將給你完全相同的分配y。這表明你使用的方法不是最適合功能識別的方法。

  2. 在LSTM中至關重要的是它允許您在輸入之間存儲信息的內存。從獨立繪製點序列(你在腳本中做什麼)完全相反。在你的方法中學習到的每一個內存關聯都可能是虛假的。

+0

謝謝馬爾欽。我想我現在得到它。此外 - 所討論的代碼在每次迭代中生成隨機k和b - 這使NN無法學習任何特徵。我看到2個可能的解決方案:1.在啓動時只生成一次k和b。這意味着我們得到一些固定線,拋物線和sqrt。 2.依次生成輸入點。 雖然(2.)是可選的。我試圖實現(1.) - 它工作!經過1000次迭代後,NN能夠以99%的精度識別功能! –