Моя простая нейронная сеть работает лучше без предвзятости. У меня проблемы с интерпретацией смещения на скрытом слое

Быстрое исправление: моя сеть лучше учится с помощью столбца смещения на входе, раньше я сталкивался с глупой ошибкой, из-за которой казалось, что смещение вредно, но на самом деле оно фактически повышает производительность по сравнению с отсутствием смещения

У меня есть нейронная сеть, реализованная с моим собственным классом матрицы. Для справки, я использовал эту же сеть, чтобы получить ~94% правильного решения проблемы классификации рукописных цифр MNIST. Рассматривая проблему с игрушками (XOR Gate), я понял, что мое представление смещения неверно, и если я его исправлю, у меня будет гораздо лучшая производительность. Также эта сеть использует стохастический мини-пакетный градиентный спуск.

Я разработал множество функций и возможностей в своих классах, чтобы они были максимально удобочитаемыми. Моя сеть хорошо учится с колонкой смещения, вставленной в матрицу ввода. Мне нужно найти способ реализовать это и на скрытом слое. Я хочу реализовать его в виде столбца на входе и в скрытом слое.

В следующем коде inputBatch содержит входы, в которые уже вставлен столбец 1. Например, проблемы ввода XOR становятся

{1, 0, 0}
{1, 0, 1}
{1, 1, 0}
{1, 1, 1}

Для этой проблемы предположим, что я использую 16 скрытых узлов. Мои выводы отформатированы так:

{0}
{1}
{1}
{0}

Поскольку мои входные данные представляют собой матрицу 4x3, моя матрица inputToHiddenWeights будет иметь размер 3x16. Это приводит к скрытому слою 4x16, к которому я поэлементно применяю сигмовидную функцию. Затем я умножаю его на матрицу hiddenToOutputWeight, которая является матрицей 16x1. Результатом этого является конечный результат, представляющий собой вектор 4x1, в котором я снова и снова применяю сигмовидную функцию. На этом теперь заканчивается прямой проход.

После просмотра этой ссылки: https://stats.stackexchange.com/questions/124651/how-do-i-incorporate-the-biases-in-my-feed-forward-neural-network

Меня убеждают, что мое входное представление правильное, я добавил столбец из них, и это пока дало мне приличную производительность, хотя я знаю, что у меня более высокая производительность, ожидая, если я смогу исправить смещение в других слоях. В конце ссылки выше пользователь добавляет дополнительный столбец единиц к скрытому слою после применения сигмоида и перед умножением на HiddenToOutputWeights (он не включает никаких шагов после добавления столбца к скрытому слою). Если бы я сделал это, это означало бы, что 1-й: вычислить скрытый слой как обычно, 2-й: добавить столбец 1 с результатом, 3-й: изменить мой HiddenToOutputWeights на размер 17x1.

Это все хорошо и даже хорошо (и даже работает для прямого прохода). Но мои проблемы возникают во время обратного распространения. Я могу сойти со всех, кроме последних шагов. Вы увидите весь код ниже, но я хочу выделить последние две операции

this.hiddenToOutputWeights = hiddenToOutputWeights.add(deltaOutput.add(momentumOutput));        

this.inputToHiddenWeights = inputToHiddenWeights.add(deltaHidden.add(momentumHidden));

В этой операции я не могу добавить deltaHidden и momentumHidden, так как deltaHidden является матрицей 3x17, а momentumHidden (и inputToHiddenWeights) имеют размер 3x16.

Моя проблема: добавление дополнительного столбца одного в скрытом слое, как видно по ссылке выше, работает для прямого распространения, но когда я добираюсь до backprop, добавленный столбец останавливает меня от выполнения моих матричных операций. Как я могу это исправить? Спасибо всем. Есть вопросы, дайте мне знать.

Вот мой код прямого распространения:

private void forwardPropagation(Matrix inputBatch, Matrix desiredLabels, boolean isTraining) {

// compute the hiddenActivation by multiplying across
// the inputToHiddenWeights and then apply the sigmoid function
// this is the transfer from the input neurons to the first hidden layer

this.hiddenActivation = (inputBatch.matrixMult(this.inputToHiddenWeights)).sigmoid();

// compute the outputActivation (the final prediction) by multing the hiddenActivation
// with the hiddenToOutput weights and then apply the sigmoid function
// this is the transfer from the middle hidden layer to the 3rd and final output layer

this.outputActivation = (hiddenActivation.matrixMult(this.hiddenToOutputWeights)).sigmoid();

// Compute error (desired - actual) 
// this is used in backprop

this.labelError = desiredLabels.sub(outputActivation);

// no reason for separating forward/backward prop other than I like how it looks

if(isTraining) {
    backPropagation(inputBatch);
}

И вот мой код backProp:

private void backPropagation(Matrix inputBatch) {

    // compute slope at output and hidden layers

    Matrix slopeOutput = outputActivation.sigmoidPrime();
    Matrix slopeHidden = hiddenActivation.sigmoidPrime();

    // compute error signals of the output and hidden layers. This is an intermediate calculation of the delta rule,
    // and because the calculation for the hidden layer use parts of the calculation already done for the output layer,
    // we can't just calculate the delta rule all at once. 
    // Note: Transposes represent accumulated dot products  

    Matrix errorSignalOutput = labelError.scalarMultAcrossMatricies(slopeOutput);
    Matrix errorSignalHidden = (errorSignalOutput.matrixMult(hiddenToOutputWeights.transpose())).scalarMultAcrossMatricies(slopeHidden);

    // calculate momentum by grabbing hold of the previous iterations deltas and multiplying it by the momentum factor which is a number from 0 to 1

    this.momentumOutput = deltaOutput.scalarMult(this.momentumFactor);
    this.momentumHidden = deltaHidden.scalarMult(this.momentumFactor);

    // compute delta for outputToHidden weights and then inputToHidden weights.
    // save the deltas so we can add a momentum term 

    this.deltaOutput = ((hiddenActivation.transpose()).matrixMult(errorSignalOutput)).scalarMult(LEARNING_RATE);
    this.deltaHidden = ((inputBatch.transpose()).matrixMult(errorSignalHidden)).scalarMult(LEARNING_RATE);

    // update weight at both output and hidden layers SIMULTANEOUSLY else you risk messing up the final result do to 
    // meddling with intermediate calculations. 

    this.hiddenToOutputWeights = hiddenToOutputWeights.add(deltaOutput.add(momentumOutput));        
    this.inputToHiddenWeights = inputToHiddenWeights.add(deltaHidden.add(momentumHidden));

    this.stepOfIteration++;
}

Скорее всего: наличие только одного столбца смещения на входе по-прежнему позволяет сети сходиться к решению. У меня точность 94% с использованием только смещения на входе и больше нигде. Я хочу найти решение этой проблемы, и у меня есть предвзятость к скрытому слою.

0 ответов

Другие вопросы по тегам