Torch Lua: 为什么我的梯度下降没有优化错误?
Torch Lua: Why is my gradient descent not optimizing the error?
我一直在尝试在 Torch/Lua 中实现孪生神经网络,如 I already explained here。
现在我有了我的第一个实现,我认为它是好的。
不幸的是,我遇到了一个问题:在训练反向传播过程中,梯度下降没有更新错误。也就是说,它始终计算相同的值(即 +1 或 -1),而不更改它。
在正确的实现中,错误应该从 +1 到 -1 或从 -1 到 +1。在我的例子中,它只是停留在上限值,没有任何变化。
为什么?我真的在寻找可以给我一些提示的人。
这是我的工作代码,您可以尝试 运行:
LEARNING_RATE_CONST = 0.01;
output_layer_number = 1;
MAX_ITERATIONS_CONST = 10;
require 'os'
require 'nn'
-- rounds a real number num to the number having idp values after the dot
function round(num, idp)
local mult = 10^(idp or 0)
return math.floor(num * mult + 0.5) / mult
end
-- gradient update for the siamese neural network
function gradientUpdate(perceptron, dataset_vector, targetValue, learningRate, max_iterations)
print('gradientUpdate()\n')
for i = 1, max_iterations do
predictionValue = perceptron:forward(dataset_vector)[1]
sys.sleep(0.2)
gradientWrtOutput = torch.Tensor({-targetValue});
perceptron:zeroGradParameters()
perceptron:backward(dataset_vector, gradientWrtOutput) --
perceptron:updateParameters(learningRate)
predictionValue = perceptron:forward(dataset_vector)[1]
io.write("i="..i..") optimization predictionValue= "..predictionValue.."\n");
if(predictionValue==targetValue) then
io.write("\t@@@ (i="..i..") optimization predictionValue "..predictionValue.." @@@\n");
break
end
end
return perceptron;
end
input_number = 6; -- they are 6
dim = 10
hiddenUnits = 3
trueTarget=1; falseTarget=-trueTarget;
trainDataset = {}; targetDataset = {};
for i=1, dim do
trainDataset[i]={torch.rand(input_number), torch.rand(input_number)}
if i%2==0 then targetDataset[i] = trueTarget
else targetDataset[i] = falseTarget
end
-- print('targetDataset['..i..'] '..targetDataset[i]);
-- sys.sleep(0.2)
end
for i=1, dim do
for j=1, input_number do
print(round(trainDataset[i][1][j],2)..' '..round(trainDataset[i][2][j],2));
end
end
-- imagine we have one network we are interested in, it is called "perceptronUpper"
perceptronUpper= nn.Sequential()
print('input_number='..input_number..'\thiddenUnits='..hiddenUnits);
perceptronUpper:add(nn.Linear(input_number, hiddenUnits))
perceptronUpper:add(nn.Tanh())
if dropOutFlag==TRUE then perceptronUpper:add(nn.Dropout()) end
perceptronUpper:add(nn.Linear(hiddenUnits,output_layer_number))
perceptronUpper:add(nn.Tanh())
perceptronLower = perceptronUpper:clone('weight', 'gradWeight', 'gradBias', 'bias')
parallel_table = nn.ParallelTable()
parallel_table:add(perceptronUpper)
parallel_table:add(perceptronLower)
perceptron= nn.Sequential()
perceptron:add(parallel_table)
perceptron:add(nn.CosineDistance())
max_iterations = MAX_ITERATIONS_CONST;
learnRate = LEARNING_RATE_CONST;
-- # TRAINING:
for k=1, dim do
print('\n[k='..k..'] gradientUpdate()');
perceptron = gradientUpdate(perceptron, trainDataset[k], targetDataset[k], learnRate, max_iterations)
end
问题是:为什么 predictionValue 变量总是相同的?为什么没有更新?
编辑:我现在意识到问题是我只使用了 1 个输出层维度。我把它移到 6,但不幸的是我有一个新问题。梯度没有在正确的方向上更新。
例如,这是使用我之前的代码 output_layer_number=6
时发生的情况
i=1) predictionValue=0.99026757478549 target=-1
i=2) predictionValue=0.9972249767451 target=-1
i=3) predictionValue=0.95910828489725 target=-1
i=4) predictionValue=0.98960431921481 target=-1
i=5) predictionValue=0.9607511165448 target=-1
i=6) predictionValue=0.7774414068913 target=-1
i=7) predictionValue=0.78994300446018 target=-1
i=8) predictionValue=0.96893163039218 target=-1
i=9) predictionValue=0.99786687264848 target=-1
i=10) predictionValue=0.92254348014872 target=-1
i=11) predictionValue=0.84935926907926 target=-1
i=12) predictionValue=0.93696147024616 target=-1
i=13) predictionValue=0.93469525917962 target=-1
i=14) predictionValue=0.9584800936415 target=-1
i=15) predictionValue=0.99376832219916 target=-1
i=16) predictionValue=0.97381161559835 target=-1
i=17) predictionValue=0.94124227912993 target=-1
i=18) predictionValue=0.94947181918451 target=-1
i=19) predictionValue=0.9946839455962 target=-1
i=20) predictionValue=0.9637013147803 target=-1
i=21) predictionValue=0.94853981221519 target=-1
i=22) predictionValue=0.95441294067747 target=-1
i=23) predictionValue=0.99999485148281 target=-1
i=24) predictionValue=0.9900480694373 target=-1
i=25) predictionValue=0.99316158138794 target=-1
也就是说,predictionValue 永远不会趋向于 -1。为什么?
why the predictionValue variable is always the same? Why doesn't it get updates?
首先,只有在 predictionValue*targetValue < 1
时才应该执行反向传播,以确保只有在需要将线对推到一起 (targetValue = 1
) 或拉开 (targetValue = -1
).
另见 torch/nn official example 说明了这一点。
话虽如此,您 只有 1 个输出单元 (output_layer_number = 1
)。这意味着你的暹罗网络的每个分支都会产生一个标量,resp。 u
和 v
。然后用余弦距离比较这对标量:
C(u,v) = cosine(u, v) = (u / |u|) x (v / |v|)
注意:此条件在这里只能取两个值:1 或 -1(见下方蓝色部分)。
当需要反向传播时,您计算此标准相对于输入的导数,即 dC/du
和 dC/dv
。但是这些 导数是 null 并且在 0 处未定义(见下面的红色部分):
这就是反向传播在这里什么都不做的原因,即它保持静态(您可以通过打印出这些导数的范数在实践中验证这一点)。
我一直在尝试在 Torch/Lua 中实现孪生神经网络,如 I already explained here。 现在我有了我的第一个实现,我认为它是好的。
不幸的是,我遇到了一个问题:在训练反向传播过程中,梯度下降没有更新错误。也就是说,它始终计算相同的值(即 +1 或 -1),而不更改它。 在正确的实现中,错误应该从 +1 到 -1 或从 -1 到 +1。在我的例子中,它只是停留在上限值,没有任何变化。
为什么?我真的在寻找可以给我一些提示的人。
这是我的工作代码,您可以尝试 运行:
LEARNING_RATE_CONST = 0.01;
output_layer_number = 1;
MAX_ITERATIONS_CONST = 10;
require 'os'
require 'nn'
-- rounds a real number num to the number having idp values after the dot
function round(num, idp)
local mult = 10^(idp or 0)
return math.floor(num * mult + 0.5) / mult
end
-- gradient update for the siamese neural network
function gradientUpdate(perceptron, dataset_vector, targetValue, learningRate, max_iterations)
print('gradientUpdate()\n')
for i = 1, max_iterations do
predictionValue = perceptron:forward(dataset_vector)[1]
sys.sleep(0.2)
gradientWrtOutput = torch.Tensor({-targetValue});
perceptron:zeroGradParameters()
perceptron:backward(dataset_vector, gradientWrtOutput) --
perceptron:updateParameters(learningRate)
predictionValue = perceptron:forward(dataset_vector)[1]
io.write("i="..i..") optimization predictionValue= "..predictionValue.."\n");
if(predictionValue==targetValue) then
io.write("\t@@@ (i="..i..") optimization predictionValue "..predictionValue.." @@@\n");
break
end
end
return perceptron;
end
input_number = 6; -- they are 6
dim = 10
hiddenUnits = 3
trueTarget=1; falseTarget=-trueTarget;
trainDataset = {}; targetDataset = {};
for i=1, dim do
trainDataset[i]={torch.rand(input_number), torch.rand(input_number)}
if i%2==0 then targetDataset[i] = trueTarget
else targetDataset[i] = falseTarget
end
-- print('targetDataset['..i..'] '..targetDataset[i]);
-- sys.sleep(0.2)
end
for i=1, dim do
for j=1, input_number do
print(round(trainDataset[i][1][j],2)..' '..round(trainDataset[i][2][j],2));
end
end
-- imagine we have one network we are interested in, it is called "perceptronUpper"
perceptronUpper= nn.Sequential()
print('input_number='..input_number..'\thiddenUnits='..hiddenUnits);
perceptronUpper:add(nn.Linear(input_number, hiddenUnits))
perceptronUpper:add(nn.Tanh())
if dropOutFlag==TRUE then perceptronUpper:add(nn.Dropout()) end
perceptronUpper:add(nn.Linear(hiddenUnits,output_layer_number))
perceptronUpper:add(nn.Tanh())
perceptronLower = perceptronUpper:clone('weight', 'gradWeight', 'gradBias', 'bias')
parallel_table = nn.ParallelTable()
parallel_table:add(perceptronUpper)
parallel_table:add(perceptronLower)
perceptron= nn.Sequential()
perceptron:add(parallel_table)
perceptron:add(nn.CosineDistance())
max_iterations = MAX_ITERATIONS_CONST;
learnRate = LEARNING_RATE_CONST;
-- # TRAINING:
for k=1, dim do
print('\n[k='..k..'] gradientUpdate()');
perceptron = gradientUpdate(perceptron, trainDataset[k], targetDataset[k], learnRate, max_iterations)
end
问题是:为什么 predictionValue 变量总是相同的?为什么没有更新?
编辑:我现在意识到问题是我只使用了 1 个输出层维度。我把它移到 6,但不幸的是我有一个新问题。梯度没有在正确的方向上更新。 例如,这是使用我之前的代码 output_layer_number=6
时发生的情况i=1) predictionValue=0.99026757478549 target=-1
i=2) predictionValue=0.9972249767451 target=-1
i=3) predictionValue=0.95910828489725 target=-1
i=4) predictionValue=0.98960431921481 target=-1
i=5) predictionValue=0.9607511165448 target=-1
i=6) predictionValue=0.7774414068913 target=-1
i=7) predictionValue=0.78994300446018 target=-1
i=8) predictionValue=0.96893163039218 target=-1
i=9) predictionValue=0.99786687264848 target=-1
i=10) predictionValue=0.92254348014872 target=-1
i=11) predictionValue=0.84935926907926 target=-1
i=12) predictionValue=0.93696147024616 target=-1
i=13) predictionValue=0.93469525917962 target=-1
i=14) predictionValue=0.9584800936415 target=-1
i=15) predictionValue=0.99376832219916 target=-1
i=16) predictionValue=0.97381161559835 target=-1
i=17) predictionValue=0.94124227912993 target=-1
i=18) predictionValue=0.94947181918451 target=-1
i=19) predictionValue=0.9946839455962 target=-1
i=20) predictionValue=0.9637013147803 target=-1
i=21) predictionValue=0.94853981221519 target=-1
i=22) predictionValue=0.95441294067747 target=-1
i=23) predictionValue=0.99999485148281 target=-1
i=24) predictionValue=0.9900480694373 target=-1
i=25) predictionValue=0.99316158138794 target=-1
也就是说,predictionValue 永远不会趋向于 -1。为什么?
why the predictionValue variable is always the same? Why doesn't it get updates?
首先,只有在 predictionValue*targetValue < 1
时才应该执行反向传播,以确保只有在需要将线对推到一起 (targetValue = 1
) 或拉开 (targetValue = -1
).
另见 torch/nn official example 说明了这一点。
话虽如此,您 只有 1 个输出单元 (output_layer_number = 1
)。这意味着你的暹罗网络的每个分支都会产生一个标量,resp。 u
和 v
。然后用余弦距离比较这对标量:
C(u,v) = cosine(u, v) = (u / |u|) x (v / |v|)
注意:此条件在这里只能取两个值:1 或 -1(见下方蓝色部分)。
当需要反向传播时,您计算此标准相对于输入的导数,即 dC/du
和 dC/dv
。但是这些 导数是 null 并且在 0 处未定义(见下面的红色部分):
这就是反向传播在这里什么都不做的原因,即它保持静态(您可以通过打印出这些导数的范数在实践中验证这一点)。