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

总结

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

较高的学习率会导致计算出的成本值变成 NaN,但这并没有破坏/降低分类器本身的性能。

我的尝试

注意:我的数据集已经通过均值归一化进行了标准化。在尝试选择我的学习率时,我从 0.1 和 400 次迭代开始。这导致了以下图表:

1 – 图表 1

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

2 – 图表 2

线条不再消失,意味着没有 NaN 值,但准确率为 87%,这显著降低了。

我又进行了两次测试,使用更多的迭代和稍微高的学习率,两次测试的图表都如预期那样随着迭代次数减少,但准确率约为 86-88%。这两次测试中也没有 NaN 值。

我意识到我的数据集是倾斜的,只有 481 封垃圾邮件和 2412 封正常邮件。因此,我计算了这些不同组合的 F 分数,希望找到后面的组合有更高的 F 分数,而准确率是由于倾斜造成的。但事实并非如此 – 我总结了我的结果在表格中:

3 – 表格

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

我能想到的唯一事情是我的准确率和 F 分数的计算有误,或者我最初对线条“消失”的调试是错误的。

编辑:这个问题关键在于为什么那些选择的学习率下会产生 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];endforgraphics_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() 函数定义如下:

返回一个标量、矩阵或 N 维数组,其所有元素都是 eps,即机器精度。

更准确地说,eps 是机器浮点系统中任何两个相邻数字之间的相对间隔。这个数字显然是系统依赖的。在支持 IEEE 浮点运算的机器上,eps 对于双精度大约是 2.2204e-16,对于单精度大约是 1.1921e-07。

当调用时有多个参数时,前两个参数被视为行数和列数,任何进一步的参数指定额外的矩阵维度。可选参数 class 指定返回类型,可以是“double”或“single”。

所以这意味着,将这个值加到之前被视为 0 的 Sigmoid 函数计算的值上,会使其成为最接近 0 但不是 0 的值,从而使 log() 不会返回 -Inf。

在测试中,使用学习率为 0.1 和迭代次数为 2000/1000/400 时,完全绘制了图表,并且在检查时没有产生 NaN 值。

**图表 1 现在**

注意:以防有人在想,在此之后准确率和 F 分数没有变化,所以尽管在计算高学习率的成本时出现了错误,准确率确实是那么好。

Related Posts

使用LSTM在Python中预测未来值

这段代码可以预测指定股票的当前日期之前的值,但不能预测…

如何在gensim的word2vec模型中查找双词组的相似性

我有一个word2vec模型,假设我使用的是googl…

dask_xgboost.predict 可以工作但无法显示 – 数据必须是一维的

我试图使用 XGBoost 创建模型。 看起来我成功地…

ML Tuning – Cross Validation in Spark

我在https://spark.apache.org/…

如何在React JS中使用fetch从REST API获取预测

我正在开发一个应用程序,其中Flask REST AP…

如何分析ML.NET中多类分类预测得分数组?

我在ML.NET中创建了一个多类分类项目。该项目可以对…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注