Я пытаюсь внедрить мини-пакетное обучение в свою нейронную сеть вместо «онлайн» стохастического метода обновления весов для каждого образца обучения.
Я разработал несколько новичков в нейронной сети, в которой я могу настроить количество нейронов в каждом слое, функции активации и т. Д. Это поможет мне понять нейронные сети. Я натренировал сеть на наборе данных 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 (), обновления веса основаны на входных значениях (в зависимости от введенной выборки) и скрытых единичных значениях (также зависящих от входной входной выборки). Так, когда у меня есть средний градиент минибата, который я распространяю обратно, как я буду обновлять веса? какие входные значения я использую?