Почему более высокая скорость обучения в логистической регрессии приводит к затратам на NaN? - PullRequest
0 голосов
/ 11 февраля 2019

Сводка

Я создаю классификатор для электронных писем со спамом и ветчиной, используя 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

Моя функция цены.код 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

код моего прогноза.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

1 Ответ

0 голосов
/ 12 февраля 2019

Кредит, где он должен быть:

Здесь мне очень помог этот ответ: 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».

Так что это означает, что добавление этого значения к значению, вычисленному функцией Sigmoid (которое ранее было так близкодля 0 было принято значение 0) будет означать, что это самое близкое к 0 значение, которое не равно 0, что делает log () не возвращаемым -Inf.

При тестировании со скоростью обучения, равной 0,1, и итерациями как2000/1000/400, был построен полный график, и при проверке не были получены значения NaN.

**Graph 1 now**

Примечание: на всякий случай, если кому-то интересно, точность и FScores непосле этого изменились, поэтому точность действительно была такой хорошей, несмотря на ошибку в расчете стоимости с более высокой скоростью обучения.

...