Как обновить вес при использовании мини-партий? - PullRequest
0 голосов
/ 23 января 2019

Я пытаюсь внедрить мини-пакетное обучение в свою нейронную сеть вместо «онлайн» стохастического метода обновления весов для каждого образца обучения.

Я разработал несколько новичков в нейронной сети, в которой я могу настроить количество нейронов в каждом слое, функции активации и т. Д. Это поможет мне понять нейронные сети. Я натренировал сеть на наборе данных mnist, но для того, чтобы выйти из строя, требуется около 200 эпох, и на тренировочном наборе частота ошибок составляет 20%, что мне кажется очень плохим. В настоящее время я использую приличный стохастический градиент для обучения сети. То, что я хотел бы попробовать, это использовать мини-партии вместо. Я понимаю концепцию, что я должен накапливать и усреднять ошибку из каждого обучающего образца, прежде чем распространять ошибку обратно. Моя проблема возникает, когда я хочу вычислить изменения, которые я должен внести в веса. Чтобы объяснить это лучше рассмотрим очень простую модель персептрона. Один вход, один скрытый слой, один выход. Чтобы вычислить изменение, которое мне нужно внести в вес между входом и скрытой единицей, я буду использовать следующее уравнение:

∂C / ∂w1 = ∂C / ∂O * ∂O / ∂h * ∂h / ∂w1

Если вы производите частичные производные, вы получаете:

∂C / ∂w1 = (ожидаемый ответ) (w2) (вход)

Теперь эта формула говорит, что вам нужно умножить обратную распространенную ошибку на вход. Для онлайн-стохастического обучения это имеет смысл, потому что вы используете 1 вход для обновления веса. Для обучения мини-партии вы использовали много входных данных, так на какой вход умножается ошибка? Я надеюсь, что вы можете помочь мне с этим.

void propogateBack(void){


    //calculate 6C/6G
    for (count=0;count<network.outputs;count++){
            network.g_error[count] = derive_cost((training.answer[training_current])-(network.g[count]));
    }



    //calculate 6G/6O
    for (count=0;count<network.outputs;count++){
        network.o_error[count] = derive_activation(network.g[count])*(network.g_error[count]);
    }


    //calculate 6O/6S3
    for (count=0;count<network.h3_neurons;count++){
        network.s3_error[count] = 0;
        for (count2=0;count2<network.outputs;count2++){
            network.s3_error[count] += (network.w4[count2][count])*(network.o_error[count2]);
        }
    }


    //calculate 6S3/6H3
    for (count=0;count<network.h3_neurons;count++){
        network.h3_error[count] = (derive_activation(network.s3[count]))*(network.s3_error[count]);
    }


    //calculate 6H3/6S2
    network.s2_error[count] = = 0;
    for (count=0;count<network.h2_neurons;count++){
        for (count2=0;count2<network.h3_neurons;count2++){ 
            network.s2_error[count] = += (network.w3[count2][count])*(network.h3_error[count2]);
        }
    }



    //calculate 6S2/6H2
    for (count=0;count<network.h2_neurons;count++){
        network.h2_error[count] = (derive_activation(network.s2[count]))*(network.s2_error[count]);
    }


    //calculate 6H2/6S1
    network.s1_error[count] = 0;
    for (count=0;count<network.h1_neurons;count++){
        for (count2=0;count2<network.h2_neurons;count2++){
            buffer += (network.w2[count2][count])*network.h2_error[count2];
        }
    }


    //calculate 6S1/6H1
    for (count=0;count<network.h1_neurons;count++){
        network.h1_error[count] = (derive_activation(network.s1[count]))*(network.s1_error[count]);

    }


}





void updateWeights(void){


    //////////////////w1
    for(count=0;count<network.h1_neurons;count++){
        for(count2=0;count2<network.inputs;count2++){
            network.w1[count][count2] -= learning_rate*(network.h1_error[count]*network.input[count2]);
        }

    }





    //////////////////w2
    for(count=0;count<network.h2_neurons;count++){
        for(count2=0;count2<network.h1_neurons;count2++){
            network.w2[count][count2] -= learning_rate*(network.h2_error[count]*network.s1[count2]);
        }

    }



    //////////////////w3
    for(count=0;count<network.h3_neurons;count++){
        for(count2=0;count2<network.h2_neurons;count2++){
            network.w3[count][count2] -= learning_rate*(network.h3_error[count]*network.s2[count2]);
        }

    }


    //////////////////w4
    for(count=0;count<network.outputs;count++){
        for(count2=0;count2<network.h3_neurons;count2++){
            network.w4[count][count2] -= learning_rate*(network.o_error[count]*network.s3[count2]);
        }

    }
}

Код, который я прикрепил, - это то, как я делаю стохастические обновления онлайн. Как вы можете видеть в функции updateWeights (), обновления веса основаны на входных значениях (в зависимости от введенной выборки) и скрытых единичных значениях (также зависящих от входной входной выборки). Так, когда у меня есть средний градиент минибата, который я распространяю обратно, как я буду обновлять веса? какие входные значения я использую?

1 Ответ

0 голосов
/ 25 января 2019

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

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

...