debugging - 为什么逻辑回归中较高的学习率会产生 NaN 成本?

标签 debugging machine-learning octave logistic-regression

摘要

我正在使用 Octave 和 Ling-Spam 语料库构建垃圾邮件与普通邮件的分类器;我的分类方法是逻辑回归。

较高的学习率会导致计算成本为 NaN 值,但它不会破坏/降低分类器本身的性能。

我的尝试

注意:我的数据集已使用均值归一化进行归一化。 当尝试选择我的学习率时,我从 0.1 和 400 次迭代开始。这导致了以下情节:

1 - 图 1

当线条在几次迭代后完全消失时,这是由于产生了 NaN 值;我认为这会导致参数值损坏,从而导致准确性较差,但在检查准确性时,我发现测试集上的准确性为 95%(这意味着梯度下降显然仍在发挥作用)。我检查了学习率和迭代的不同值,以查看图表如何变化:

2 - 图 2

线条不再消失,意味着没有 NaN 值,但准确度为 87%,这要低得多。

我又做了两次测试,迭代次数更多,学习率略高,在这两次测试中,图形都按预期随着迭代次数而减少,但准确率约为 86-88%。那里也没有 NaN。

我意识到我的数据集存在偏差,只有 481 封垃圾邮件和 2412 封正常邮件。因此,我计算了每个不同组合的 FScore,希望发现后面的组合具有更高的 FScore,而准确性是由于偏差造成的。情况也并非如此 - 我已在表格中总结了我的结果:

3 - 表

所以不存在过拟合,倾斜似乎也不是问题;我现在不知道该怎么办!

我唯一能想到的是我对准确度和 FScore 的计算是错误的,或者是我对“消失”行的初始调试是错误的。

编辑:这个问题的关键在于为什么那些选择的学习率会出现 NaN 值。因此,我降低学习率的临时解决方案并没有真正回答我的问题 - 我一直认为较高的学习率只是发散而不是收敛,产生 NaN 值。

我的代码

我的 main.m 代码(禁止从文件中获取数据集):

numRecords = length(labels);

trainingSize = ceil(numRecords*0.6);
CVSize = trainingSize + ceil(numRecords*0.2);

featureData = normalise(data);

featureData = [ones(numRecords, 1), featureData];

numFeatures = size(featureData, 2);

featuresTrain = featureData(1:(trainingSize-1),:);
featuresCV = featureData(trainingSize:(CVSize-1),:);
featuresTest = featureData(CVSize:numRecords,:);

labelsTrain = labels(1:(trainingSize-1),:);
labelsCV = labels(trainingSize:(CVSize-1),:);
labelsTest = labels(CVSize:numRecords,:);

paramStart = zeros(numFeatures, 1);

learningRate = 0.0001;
iterations = 400;

[params] = gradDescent(featuresTrain, labelsTrain, learningRate, iterations, paramStart, featuresCV, labelsCV);

threshold = 0.5;
[accuracy, precision, recall] = predict(featuresTest, labelsTest, params, threshold);
fScore = (2*precision*recall)/(precision+recall);

我的 gradDescent.m 代码:​​

function [optimParams] = gradDescent(features, labels, learningRate, iterations, paramStart, featuresCV, labelsCV)

x_axis = [];
J_axis = [];
J_CV = [];

params = paramStart;

for i=1:iterations,
  [cost, grad] = costFunction(features, labels, params);
  [cost_CV] = costFunction(featuresCV, labelsCV, params);

  params = params - (learningRate.*grad);

  x_axis = [x_axis;i];
  J_axis = [J_axis;cost];
  J_CV = [J_CV;cost_CV];
endfor

graphics_toolkit("gnuplot")
plot(x_axis, J_axis, 'r', x_axis, J_CV, 'b');
legend("Training", "Cross-Validation");
xlabel("Iterations");
ylabel("Cost");
title("Cost as a function of iterations");

optimParams = params;
endfunction

我的 costFunction.m 代码:​​

function [cost, grad] = costFunction(features, labels, params)
  numRecords = length(labels);

  hypothesis = sigmoid(features*params);

  cost = (-1/numRecords)*sum((labels).*log(hypothesis)+(1-labels).*log(1-hypothesis));

  grad = (1/numRecords)*(features'*(hypothesis-labels));
endfunction

我的predict.m代码:

function [accuracy, precision, recall] = predict(features, labels, params, threshold)
numRecords=length(labels);

predictions = sigmoid(features*params)>threshold;

correct = predictions == labels;

truePositives = sum(predictions == labels == 1);
falsePositives = sum((predictions == 1) != labels);
falseNegatives = sum((predictions == 0) != labels);

precision = truePositives/(truePositives+falsePositives);
recall = truePositives/(truePositives+falseNegatives);

accuracy = 100*(sum(correct)/numRecords);
endfunction

最佳答案

应得的信用:

这个答案有很大帮助:https://stackoverflow.com/a/51896895/8959704所以这个问题有点重复,但我没有意识到这一点,而且一开始并不明显......我会尽力解释为什么该解决方案也有效,以避免简单地复制答案。

解决方案:

问题实际上是我的数据中出现 0*log(0) = NaN 结果。为了解决这个问题,在我的成本计算中,它变成了:

cost = (-1/numRecords)*sum((labels).*log(hypothesis)+(1-labels).*log(1-hypothesis+eps(numRecords, 1)));

(请参阅变量值等的问题,当仅此行发生变化时包含其余部分似乎是多余的)

说明:

eps()函数定义如下:

Return a scalar, matrix or N-dimensional array whose elements are all eps, the machine precision.

More precisely, eps is the relative spacing between any two adjacent numbers in the machine’s floating point system. This number is obviously system dependent. On machines that support IEEE floating point arithmetic, eps is approximately 2.2204e-16 for double precision and 1.1921e-07 for single precision.

When called with more than one argument the first two arguments are taken as the number of rows and columns and any further arguments specify additional matrix dimensions. The optional argument class specifies the return type and may be either "double" or "single".

因此,这意味着将此值添加到 Sigmoid 函数计算的值(之前非常接近 0,因此被视为 0)将意味着它是最接近 0 且不为 0 的值,从而使 log () 不返回 -Inf。

当学习率为 0.1、迭代次数为 2000/1000/400 进行测试时,绘制了完整的图表,并且在检查时没有产生 NaN 值。

**Graph 1 now**

注意:以防万一有人想知道,此后准确性和 FScore 没有变化,因此尽管在较高学习率下计算成本时出现错误,但准确性确实很好。

关于debugging - 为什么逻辑回归中较高的学习率会产生 NaN 成本?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54621796/

相关文章:

linux - Debian/Ubuntu 的调试信息文件约定?

sql - Oracle 调试技术

python - [统计模型] : How can I get statsmodel to return the pvalue of an OLS object?

python - 我正在做一些 HOG 特征提取并收到此错误 IndexError : too many indices for array

matlab - matlab和octave中矩阵编程和图像处理的区别

syntax - Octave 中变量前面的@符号的目的是什么?

silverlight - 如何在 Silverlight 中调试 XAML 解析错误?

debugging - 在 swift 中重载字符串转换的规范方法是什么?

html - 识别 html 中显着相似性的算法

matlab - 矩阵中的前 n 行?